From f38861c80cec6133af499ab7655f598064f7941d Mon Sep 17 00:00:00 2001 From: Myx Date: Sat, 12 Oct 2024 17:08:43 +0200 Subject: [PATCH] Test queue --- .../bytefy.image/ConversaionQueueService.cs | 48 +++++++++++++++ services/bytefy.image/bytefy.image/Program.cs | 60 +++++++++++-------- .../bytefy.image/bytefy.image.csproj | 2 +- tools/src/app/app.routes.ts | 12 ++-- .../ascii-to-text.component.html | 0 .../ascii-to-text.component.scss | 0 .../ascii-to-text/ascii-to-text.component.ts | 2 +- .../base64-converter.component.html | 0 .../base64-converter.component.scss | 0 .../base64-converter.component.spec.ts | 0 .../base64-converter.component.ts | 2 +- .../dds-to-png/dds-to-png.component.html | 0 .../dds-to-png/dds-to-png.component.scss | 0 .../dds-to-png/dds-to-png.component.spec.ts | 0 .../dds-to-png/dds-to-png.component.ts | 2 +- .../dds-to-png/dds-to-png.service.ts | 0 .../guid/guid.component.html | 0 .../guid/guid.component.scss | 0 .../{ => client-side}/guid/guid.component.ts | 0 .../jwt-to-json/jwt-to-json.component.html | 0 .../jwt-to-json/jwt-to-json.component.scss | 0 .../jwt-to-json/jwt-to-json.component.spec.ts | 0 .../jwt-to-json/jwt-to-json.component.ts | 2 +- .../text-to-cron/text-to-cron.component.html | 0 .../text-to-cron/text-to-cron.component.scss | 0 .../text-to-cron.component.spec.ts | 0 .../text-to-cron/text-to-cron.component.ts | 2 +- .../iamge-converter.service.ts | 31 ++++++++++ .../image-converter.component.html | 3 + .../image-converter.component.scss | 0 .../image-converter.component.spec.ts | 28 +++++++++ .../image-converter.component.ts | 10 ++++ 32 files changed, 167 insertions(+), 37 deletions(-) create mode 100644 services/bytefy.image/bytefy.image/ConversaionQueueService.cs rename tools/src/tools/{ => client-side}/ascii-to-text/ascii-to-text.component.html (100%) rename tools/src/tools/{ => client-side}/ascii-to-text/ascii-to-text.component.scss (100%) rename tools/src/tools/{ => client-side}/ascii-to-text/ascii-to-text.component.ts (90%) rename tools/src/tools/{ => client-side}/base64-converter/base64-converter.component.html (100%) rename tools/src/tools/{ => client-side}/base64-converter/base64-converter.component.scss (100%) rename tools/src/tools/{ => client-side}/base64-converter/base64-converter.component.spec.ts (100%) rename tools/src/tools/{ => client-side}/base64-converter/base64-converter.component.ts (85%) rename tools/src/tools/{ => client-side}/dds-to-png/dds-to-png.component.html (100%) rename tools/src/tools/{ => client-side}/dds-to-png/dds-to-png.component.scss (100%) rename tools/src/tools/{ => client-side}/dds-to-png/dds-to-png.component.spec.ts (100%) rename tools/src/tools/{ => client-side}/dds-to-png/dds-to-png.component.ts (93%) rename tools/src/tools/{ => client-side}/dds-to-png/dds-to-png.service.ts (100%) rename tools/src/tools/{ => client-side}/guid/guid.component.html (100%) rename tools/src/tools/{ => client-side}/guid/guid.component.scss (100%) rename tools/src/tools/{ => client-side}/guid/guid.component.ts (100%) rename tools/src/tools/{ => client-side}/jwt-to-json/jwt-to-json.component.html (100%) rename tools/src/tools/{ => client-side}/jwt-to-json/jwt-to-json.component.scss (100%) rename tools/src/tools/{ => client-side}/jwt-to-json/jwt-to-json.component.spec.ts (100%) rename tools/src/tools/{ => client-side}/jwt-to-json/jwt-to-json.component.ts (91%) rename tools/src/tools/{ => client-side}/text-to-cron/text-to-cron.component.html (100%) rename tools/src/tools/{ => client-side}/text-to-cron/text-to-cron.component.scss (100%) rename tools/src/tools/{ => client-side}/text-to-cron/text-to-cron.component.spec.ts (100%) rename tools/src/tools/{ => client-side}/text-to-cron/text-to-cron.component.ts (94%) create mode 100644 tools/src/tools/server-side/image-converter/iamge-converter.service.ts create mode 100644 tools/src/tools/server-side/image-converter/image-converter.component.html create mode 100644 tools/src/tools/server-side/image-converter/image-converter.component.scss create mode 100644 tools/src/tools/server-side/image-converter/image-converter.component.spec.ts create mode 100644 tools/src/tools/server-side/image-converter/image-converter.component.ts diff --git a/services/bytefy.image/bytefy.image/ConversaionQueueService.cs b/services/bytefy.image/bytefy.image/ConversaionQueueService.cs new file mode 100644 index 0000000..7c7a5fd --- /dev/null +++ b/services/bytefy.image/bytefy.image/ConversaionQueueService.cs @@ -0,0 +1,48 @@ +using System.Collections.Concurrent; +using ImageMagick; + +namespace bytefy.image; + +public class ConversionTask +{ + public byte[] ImageData { get; set; } + public MagickFormat Format { get; set; } +} + +public class ConversionQueueService : BackgroundService +{ + private readonly ConcurrentQueue<(ConversionTask Task, TaskCompletionSource<(byte[], string)> CompletionSource)> _queue = new(); + + public void QueueConversion(ConversionTask task, TaskCompletionSource<(byte[], string)> completionSource) + { + _queue.Enqueue((task, completionSource)); + } + + protected override async Task ExecuteAsync(CancellationToken stoppingToken) + { + while (!stoppingToken.IsCancellationRequested) + { + if (_queue.TryDequeue(out var item)) + { + var result = await ProcessConversionAsync(item.Task); + item.CompletionSource.SetResult(result); + } + else + { + await Task.Delay(50, stoppingToken); // Shorten the delay to check the queue more frequently + } + } + } + + private Task<(byte[], string)> ProcessConversionAsync(ConversionTask task) + { + using var magickImage = new MagickImage(task.ImageData); + magickImage.Format = task.Format; + var resultStream = new MemoryStream(); + magickImage.Write(resultStream); + resultStream.Position = 0; + + var mimeType = MimeTypes.MimeTypeMap.GetMimeType($"image/{task.Format.ToString().ToLower()}"); + return Task.FromResult((resultStream.ToArray(), mimeType)); + } +} \ No newline at end of file diff --git a/services/bytefy.image/bytefy.image/Program.cs b/services/bytefy.image/bytefy.image/Program.cs index b23d890..cb6502e 100644 --- a/services/bytefy.image/bytefy.image/Program.cs +++ b/services/bytefy.image/bytefy.image/Program.cs @@ -1,42 +1,44 @@ +using bytefy.image; using ImageMagick; using Microsoft.AspNetCore.Antiforgery; var builder = WebApplication.CreateBuilder(args); builder.Services.AddAntiforgery(options => options.HeaderName = "2311d8d8-607d-4747-8939-1bde65643254"); +builder.Services.AddSingleton(); +builder.Services.AddHostedService(provider => provider.GetRequiredService()); + var app = builder.Build(); app.UseAntiforgery(); +var conversionQueue = app.Services.GetRequiredService(); + app.MapPost("/convert/{format}", async (IFormFile image, string format) => { - try + if (!Enum.TryParse(format, true, out MagickFormat magickFormat) || magickFormat == MagickFormat.Unknown) + return Results.BadRequest("Invalid format"); + + if (image == null || image.Length == 0) + return Results.BadRequest("No image provided"); + + if (image.Length > 20 * 1024 * 1024) + throw new Exception("Image size too large"); + + using var memoryStream = new MemoryStream(); + await image.CopyToAsync(memoryStream); + + var conversionTask = new ConversionTask { - if (!Enum.TryParse(format, true, out MagickFormat magickFormat) || magickFormat == MagickFormat.Unknown) - return Results.BadRequest("Invalid format"); + ImageData = memoryStream.ToArray(), + Format = magickFormat + }; - if (image == null || image.Length == 0) - return Results.BadRequest("No image provided"); + var tcs = new TaskCompletionSource<(byte[], string)>(); + conversionQueue.QueueConversion(conversionTask, tcs); - if (image.Length > 20 * 1024 * 1024) - throw new Exception("Image size too large"); + var (imageData, mimeType) = await tcs.Task; - using var memoryStream = new MemoryStream(); - await image.CopyToAsync(memoryStream); - var magickImage = new MagickImage(memoryStream.ToArray()); - magickImage.Format = magickFormat; - - var resultStream = new MemoryStream(); - magickImage.Write(resultStream); - resultStream.Position = 0; - - var mimeType = MimeTypes.MimeTypeMap.GetMimeType($"image/{magickFormat.ToString().ToLower()}"); - var fileName = Path.GetFileNameWithoutExtension(image.FileName); - - return Results.File(resultStream, mimeType, fileDownloadName: $"{fileName}.{magickFormat.ToString().ToLower()}"); - } - catch (NotSupportedException) { - return Results.BadRequest(); - } + return Results.File(new MemoryStream(imageData), mimeType, $"{Path.GetFileNameWithoutExtension(image.FileName)}.{magickFormat.ToString().ToLower()}"); }); app.MapGet("/antiforgery/token", (IAntiforgery forgeryService, HttpContext context) => @@ -46,4 +48,12 @@ app.MapGet("/antiforgery/token", (IAntiforgery forgeryService, HttpContext conte return TypedResults.Content(xsrfToken, "text/plain"); }); -app.Run(); \ No newline at end of file +app.MapGet("/formats", () => +{ + var formats = Enum.GetNames().ToList(); + + formats.Remove("Unknown"); + return Results.Ok(formats); +}); + +app.Run(); diff --git a/services/bytefy.image/bytefy.image/bytefy.image.csproj b/services/bytefy.image/bytefy.image/bytefy.image.csproj index 62444e4..28af9cc 100644 --- a/services/bytefy.image/bytefy.image/bytefy.image.csproj +++ b/services/bytefy.image/bytefy.image/bytefy.image.csproj @@ -5,7 +5,7 @@ enable enable true - true + false Linux diff --git a/tools/src/app/app.routes.ts b/tools/src/app/app.routes.ts index 5c497a1..d144128 100644 --- a/tools/src/app/app.routes.ts +++ b/tools/src/app/app.routes.ts @@ -1,10 +1,10 @@ import { Routes } from '@angular/router'; -import { AsciiToTextComponent } from '../tools/ascii-to-text/ascii-to-text.component'; -import { GuidComponent } from '../tools/guid/guid.component'; -import { Base64ConverterComponent } from '../tools/base64-converter/base64-converter.component'; -import { JwtToJsonComponent } from '../tools/jwt-to-json/jwt-to-json.component'; -import { TextToCronComponent } from '../tools/text-to-cron/text-to-cron.component'; -import { DdsToPngComponent } from '../tools/dds-to-png/dds-to-png.component'; +import { AsciiToTextComponent } from '../tools/client-side/ascii-to-text/ascii-to-text.component'; +import { GuidComponent } from '../tools/client-side/guid/guid.component'; +import { Base64ConverterComponent } from '../tools/client-side/base64-converter/base64-converter.component'; +import { JwtToJsonComponent } from '../tools/client-side/jwt-to-json/jwt-to-json.component'; +import { TextToCronComponent } from '../tools/client-side/text-to-cron/text-to-cron.component'; +import { DdsToPngComponent } from '../tools/client-side/dds-to-png/dds-to-png.component'; export const routes: Routes = [ { diff --git a/tools/src/tools/ascii-to-text/ascii-to-text.component.html b/tools/src/tools/client-side/ascii-to-text/ascii-to-text.component.html similarity index 100% rename from tools/src/tools/ascii-to-text/ascii-to-text.component.html rename to tools/src/tools/client-side/ascii-to-text/ascii-to-text.component.html diff --git a/tools/src/tools/ascii-to-text/ascii-to-text.component.scss b/tools/src/tools/client-side/ascii-to-text/ascii-to-text.component.scss similarity index 100% rename from tools/src/tools/ascii-to-text/ascii-to-text.component.scss rename to tools/src/tools/client-side/ascii-to-text/ascii-to-text.component.scss diff --git a/tools/src/tools/ascii-to-text/ascii-to-text.component.ts b/tools/src/tools/client-side/ascii-to-text/ascii-to-text.component.ts similarity index 90% rename from tools/src/tools/ascii-to-text/ascii-to-text.component.ts rename to tools/src/tools/client-side/ascii-to-text/ascii-to-text.component.ts index 1a2d847..5c031bf 100644 --- a/tools/src/tools/ascii-to-text/ascii-to-text.component.ts +++ b/tools/src/tools/client-side/ascii-to-text/ascii-to-text.component.ts @@ -1,5 +1,5 @@ import { Component } from '@angular/core'; -import { DualTextareaComponent } from '../../app/shared/dual-textarea/dual-textarea.component'; +import { DualTextareaComponent } from '../../../app/shared/dual-textarea/dual-textarea.component'; @Component({ selector: 'app-ascii-to-text', diff --git a/tools/src/tools/base64-converter/base64-converter.component.html b/tools/src/tools/client-side/base64-converter/base64-converter.component.html similarity index 100% rename from tools/src/tools/base64-converter/base64-converter.component.html rename to tools/src/tools/client-side/base64-converter/base64-converter.component.html diff --git a/tools/src/tools/base64-converter/base64-converter.component.scss b/tools/src/tools/client-side/base64-converter/base64-converter.component.scss similarity index 100% rename from tools/src/tools/base64-converter/base64-converter.component.scss rename to tools/src/tools/client-side/base64-converter/base64-converter.component.scss diff --git a/tools/src/tools/base64-converter/base64-converter.component.spec.ts b/tools/src/tools/client-side/base64-converter/base64-converter.component.spec.ts similarity index 100% rename from tools/src/tools/base64-converter/base64-converter.component.spec.ts rename to tools/src/tools/client-side/base64-converter/base64-converter.component.spec.ts diff --git a/tools/src/tools/base64-converter/base64-converter.component.ts b/tools/src/tools/client-side/base64-converter/base64-converter.component.ts similarity index 85% rename from tools/src/tools/base64-converter/base64-converter.component.ts rename to tools/src/tools/client-side/base64-converter/base64-converter.component.ts index 2897182..bd46710 100644 --- a/tools/src/tools/base64-converter/base64-converter.component.ts +++ b/tools/src/tools/client-side/base64-converter/base64-converter.component.ts @@ -1,5 +1,5 @@ import { Component } from '@angular/core'; -import { DualTextareaComponent } from '../../app/shared/dual-textarea/dual-textarea.component'; +import { DualTextareaComponent } from '../../../app/shared/dual-textarea/dual-textarea.component'; @Component({ selector: 'app-base64-converter', diff --git a/tools/src/tools/dds-to-png/dds-to-png.component.html b/tools/src/tools/client-side/dds-to-png/dds-to-png.component.html similarity index 100% rename from tools/src/tools/dds-to-png/dds-to-png.component.html rename to tools/src/tools/client-side/dds-to-png/dds-to-png.component.html diff --git a/tools/src/tools/dds-to-png/dds-to-png.component.scss b/tools/src/tools/client-side/dds-to-png/dds-to-png.component.scss similarity index 100% rename from tools/src/tools/dds-to-png/dds-to-png.component.scss rename to tools/src/tools/client-side/dds-to-png/dds-to-png.component.scss diff --git a/tools/src/tools/dds-to-png/dds-to-png.component.spec.ts b/tools/src/tools/client-side/dds-to-png/dds-to-png.component.spec.ts similarity index 100% rename from tools/src/tools/dds-to-png/dds-to-png.component.spec.ts rename to tools/src/tools/client-side/dds-to-png/dds-to-png.component.spec.ts diff --git a/tools/src/tools/dds-to-png/dds-to-png.component.ts b/tools/src/tools/client-side/dds-to-png/dds-to-png.component.ts similarity index 93% rename from tools/src/tools/dds-to-png/dds-to-png.component.ts rename to tools/src/tools/client-side/dds-to-png/dds-to-png.component.ts index a7b196e..88537d2 100644 --- a/tools/src/tools/dds-to-png/dds-to-png.component.ts +++ b/tools/src/tools/client-side/dds-to-png/dds-to-png.component.ts @@ -1,5 +1,5 @@ import { Component, OnInit } from '@angular/core'; -import { FileConverterComponent } from '../../app/shared/upload/file-converter.component'; +import { FileConverterComponent } from '../../../app/shared/upload/file-converter.component'; import { DdsToPngService } from './dds-to-png.service'; interface ProcessedFile { diff --git a/tools/src/tools/dds-to-png/dds-to-png.service.ts b/tools/src/tools/client-side/dds-to-png/dds-to-png.service.ts similarity index 100% rename from tools/src/tools/dds-to-png/dds-to-png.service.ts rename to tools/src/tools/client-side/dds-to-png/dds-to-png.service.ts diff --git a/tools/src/tools/guid/guid.component.html b/tools/src/tools/client-side/guid/guid.component.html similarity index 100% rename from tools/src/tools/guid/guid.component.html rename to tools/src/tools/client-side/guid/guid.component.html diff --git a/tools/src/tools/guid/guid.component.scss b/tools/src/tools/client-side/guid/guid.component.scss similarity index 100% rename from tools/src/tools/guid/guid.component.scss rename to tools/src/tools/client-side/guid/guid.component.scss diff --git a/tools/src/tools/guid/guid.component.ts b/tools/src/tools/client-side/guid/guid.component.ts similarity index 100% rename from tools/src/tools/guid/guid.component.ts rename to tools/src/tools/client-side/guid/guid.component.ts diff --git a/tools/src/tools/jwt-to-json/jwt-to-json.component.html b/tools/src/tools/client-side/jwt-to-json/jwt-to-json.component.html similarity index 100% rename from tools/src/tools/jwt-to-json/jwt-to-json.component.html rename to tools/src/tools/client-side/jwt-to-json/jwt-to-json.component.html diff --git a/tools/src/tools/jwt-to-json/jwt-to-json.component.scss b/tools/src/tools/client-side/jwt-to-json/jwt-to-json.component.scss similarity index 100% rename from tools/src/tools/jwt-to-json/jwt-to-json.component.scss rename to tools/src/tools/client-side/jwt-to-json/jwt-to-json.component.scss diff --git a/tools/src/tools/jwt-to-json/jwt-to-json.component.spec.ts b/tools/src/tools/client-side/jwt-to-json/jwt-to-json.component.spec.ts similarity index 100% rename from tools/src/tools/jwt-to-json/jwt-to-json.component.spec.ts rename to tools/src/tools/client-side/jwt-to-json/jwt-to-json.component.spec.ts diff --git a/tools/src/tools/jwt-to-json/jwt-to-json.component.ts b/tools/src/tools/client-side/jwt-to-json/jwt-to-json.component.ts similarity index 91% rename from tools/src/tools/jwt-to-json/jwt-to-json.component.ts rename to tools/src/tools/client-side/jwt-to-json/jwt-to-json.component.ts index 79a2f41..9a9f7b5 100644 --- a/tools/src/tools/jwt-to-json/jwt-to-json.component.ts +++ b/tools/src/tools/client-side/jwt-to-json/jwt-to-json.component.ts @@ -1,5 +1,5 @@ import { Component } from '@angular/core'; -import { DualTextareaComponent } from '../../app/shared/dual-textarea/dual-textarea.component'; +import { DualTextareaComponent } from '../../../app/shared/dual-textarea/dual-textarea.component'; @Component({ selector: 'app-jwt-to-json', diff --git a/tools/src/tools/text-to-cron/text-to-cron.component.html b/tools/src/tools/client-side/text-to-cron/text-to-cron.component.html similarity index 100% rename from tools/src/tools/text-to-cron/text-to-cron.component.html rename to tools/src/tools/client-side/text-to-cron/text-to-cron.component.html diff --git a/tools/src/tools/text-to-cron/text-to-cron.component.scss b/tools/src/tools/client-side/text-to-cron/text-to-cron.component.scss similarity index 100% rename from tools/src/tools/text-to-cron/text-to-cron.component.scss rename to tools/src/tools/client-side/text-to-cron/text-to-cron.component.scss diff --git a/tools/src/tools/text-to-cron/text-to-cron.component.spec.ts b/tools/src/tools/client-side/text-to-cron/text-to-cron.component.spec.ts similarity index 100% rename from tools/src/tools/text-to-cron/text-to-cron.component.spec.ts rename to tools/src/tools/client-side/text-to-cron/text-to-cron.component.spec.ts diff --git a/tools/src/tools/text-to-cron/text-to-cron.component.ts b/tools/src/tools/client-side/text-to-cron/text-to-cron.component.ts similarity index 94% rename from tools/src/tools/text-to-cron/text-to-cron.component.ts rename to tools/src/tools/client-side/text-to-cron/text-to-cron.component.ts index 8b839d7..bd0fc37 100644 --- a/tools/src/tools/text-to-cron/text-to-cron.component.ts +++ b/tools/src/tools/client-side/text-to-cron/text-to-cron.component.ts @@ -1,5 +1,5 @@ import { Component } from '@angular/core'; -import { DualTextareaComponent } from '../../app/shared/dual-textarea/dual-textarea.component'; +import { DualTextareaComponent } from '../../../app/shared/dual-textarea/dual-textarea.component'; @Component({ selector: 'app-text-to-cron', diff --git a/tools/src/tools/server-side/image-converter/iamge-converter.service.ts b/tools/src/tools/server-side/image-converter/iamge-converter.service.ts new file mode 100644 index 0000000..5676ddc --- /dev/null +++ b/tools/src/tools/server-side/image-converter/iamge-converter.service.ts @@ -0,0 +1,31 @@ +import { Injectable } from '@angular/core'; +import { HttpClient, HttpHeaders } from '@angular/common/http'; +import { Observable, tap } from 'rxjs'; + +@Injectable({ + providedIn: 'root' +}) +export class ImageService { + private baseUrl = 'http://localhost:1337'; // replace with your API base URL + + constructor(private http: HttpClient) { } + + convertImage(image: File, format: string): Observable { + const formData = new FormData(); + formData.append('image', image); + + let imgToken = localStorage.getItem('imgToken'); + + const headers = new HttpHeaders({ + '2311d8d8-607d-4747-8939-1bde65643254': imgToken! + }); + + return this.http.post(`${this.baseUrl}/convert/${format}`, formData, { headers }); + } + + seteAntiforgeryToken(): void { + this.http.get(`${this.baseUrl}/antiforgery/token`, { responseType: 'text' as 'json' }).pipe( + tap(token => localStorage.setItem('imgToken', token)) + ) + } +} \ No newline at end of file diff --git a/tools/src/tools/server-side/image-converter/image-converter.component.html b/tools/src/tools/server-side/image-converter/image-converter.component.html new file mode 100644 index 0000000..fe28b53 --- /dev/null +++ b/tools/src/tools/server-side/image-converter/image-converter.component.html @@ -0,0 +1,3 @@ +

+ image-converter works! +

diff --git a/tools/src/tools/server-side/image-converter/image-converter.component.scss b/tools/src/tools/server-side/image-converter/image-converter.component.scss new file mode 100644 index 0000000..e69de29 diff --git a/tools/src/tools/server-side/image-converter/image-converter.component.spec.ts b/tools/src/tools/server-side/image-converter/image-converter.component.spec.ts new file mode 100644 index 0000000..61afcea --- /dev/null +++ b/tools/src/tools/server-side/image-converter/image-converter.component.spec.ts @@ -0,0 +1,28 @@ +/* tslint:disable:no-unused-variable */ +import { async, ComponentFixture, TestBed } from '@angular/core/testing'; +import { By } from '@angular/platform-browser'; +import { DebugElement } from '@angular/core'; + +import { ImageConverterComponent } from './image-converter.component'; + +describe('ImageConverterComponent', () => { + let component: ImageConverterComponent; + let fixture: ComponentFixture; + + beforeEach(async(() => { + TestBed.configureTestingModule({ + declarations: [ ImageConverterComponent ] + }) + .compileComponents(); + })); + + beforeEach(() => { + fixture = TestBed.createComponent(ImageConverterComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/tools/src/tools/server-side/image-converter/image-converter.component.ts b/tools/src/tools/server-side/image-converter/image-converter.component.ts new file mode 100644 index 0000000..b770711 --- /dev/null +++ b/tools/src/tools/server-side/image-converter/image-converter.component.ts @@ -0,0 +1,10 @@ +import { Component, OnInit } from '@angular/core'; + +@Component({ + selector: 'app-image-converter', + templateUrl: 'image-converter.component.html', + styleUrls: ['image-converter.component.scss'] +}) +export class ImageConverterComponent { + +}