Изменение размера изображений перед загрузкой в ​​Blazor Web Assembly

  • Михаил
  • 12 мин. на прочтение
  • 132
  • 24 Nov 2022
  • 24 Nov 2022

Итак, вы разрешаете пользователям загружать изображения на сервер из вашего приложения Blazor WASM, но по какой-то причине хотите ограничить фактические размеры изображения. Вместо того, чтобы просить пользователя отредактировать изображение перед загрузкой, вы решаете позаботиться о применении этого требования в приложении. И вы также решаете изменить размер в браузере перед загрузкой, чтобы итоговая загрузка была меньше, и вам не приходилось тратить ресурсы сервера на эту процедуру. В этой статье я расскажу, как это сделать и как получить размеры файла изображения.

 

При изменении размера файлов изображений в Blazor Web Assembly мы можем использовать метод расширения для IBrowserFileтипа, который представляет файл, представленный элементу управления загрузкой файлов в приложении Blazor, RequestImageFileAsync. Этот метод был представлен в .NET 5, поэтому он не будет работать в приложениях, созданных с использованием .NET 3.2 (если они действительно существуют).

В качестве аргументов метод принимает формат файла (тип содержимого), максимальную ширину и максимальную высоту. Внутри метод использует слой JavaScript для изменения размера изображения. В частности, он использует API холста HTML5. Он сравнивает фактические размеры изображения с аргументами максимальной ширины и высоты и применяет меньшее соотношение к обоим размерам при изменении размера, тем самым сохраняя исходные пропорции изображения.

В следующем коде используется компонент Razor, который действует как страница с возможностью навигации. Он включает InputFileкомпонент, к которому применен acceptатрибут для ограничения типов файлов, доступных для встроенного средства выбора файлов. Это удобно для пользователя, и на него не следует полагаться при проверке загружаемых типов файлов. Когда содержимое элемента управления загрузкой файла изменяется, HandleChangeвызывается обратный вызов. Это подтверждает, что отправленный файл на самом деле является файлом изображения, проверяя его тип содержимого с помощью массива разрешенных значений, а затем для RequestImageFileAsyncнего вызывается метод. В этом примере я решил, что максимальная ширина любого изображения должна составлять 300 пикселей. Они могут быть любой высоты, поэтому int.MaxValueпередаются в maxHeight параметр.

@page "/resize-image-demo"

@inject HttpClient http
Resize Image

@code {
   string[] imageTypes = new[] { "image.jpeg", "image/png" };
   async Task HandleChange(InputFileChangeEventArgs e)
   {
       if (imageTypes.Contains(e.File.ContentType))
       {
           var resized = await e.File.RequestImageFileAsync(e.File.ContentType, 300, int.MaxValue);
           var formContent = new MultipartFormDataContent
           {
               { new StreamContent(e.File.OpenReadStream(e.File.Size)), "upload", e.File.Name },
               { new StringContent(e.File.ContentType), "content-type" }
           };
           await http.PostAsync("/upload", formContent);
       }
   }
}

При использовании RequestImageFileAsyncметода будут обработаны все файлы независимо от их исходного размера и типа. Если методу передается тип файла, который не поддерживается реализацией холста в вашем браузере, приложение тихо падает (в моем тестировании). Если исходная ширина загруженного изображения составляет 300 пикселей или меньше, оно все равно обрабатывается, и возвращается новое изображение того же размера. Вместо того, чтобы отправлять каждый файл в процесс изменения размера, независимо от размера, вы можете проверить размеры изображения, чтобы увидеть, не превышают ли они ваш максимум, так как же получить ширину и высоту файла изображения?

Получение размеров изображения

Вы можете рассмотреть возможность использования кроссплатформенной библиотеки изображений C# в своем проекте WASM, такой как ImageSharp , и хотя я обнаружил, что простое получение размеров изображения прекрасно работает с этой библиотекой, ImageSharp не рекомендуется использовать в среде Blazor WASM . Вместо этого вы можете использовать взаимодействие JavaScript, чтобы вместо этого использовать API-интерфейсы браузера. Следующий код JavaScript написан в виде модуля, чтобы мы могли воспользоваться преимуществами поддержки изоляции JavaScript в Blazor . Функция принимает поток в качестве параметра и использует его для создания BLOB-объекта, из которого создается URL-адрес объекта. URL-адрес объекта назначается как srcфайл Image. Мы подключаемся к onloadсобытию изображения и получаем размеры загруженного изображения. Мы используемPromiseчтобы разрешить возвращаемое значение onloadобработчика событий изображения и сериализовать его в JSON. Мы сохраняем этот модуль как fileSize.js в папке wwwroot/js .

export async function getImageDimensions(content) {
   const url = URL.createObjectURL(new Blob([await content.arrayBuffer()]))
   const dimensions = await new Promise(resolve => {
       const img = new Image();
       img.onload = function () {
           const data = { Width: img.naturalWidth, Height: img.naturalHeight };
           resolve(data);
       };
       img.src = url;
   });
   return JSON.stringify(dimensions);
}

Далее нам нужно изменить наш компонент для работы с внедренным файлом IJSRuntime. Нам также нужно добавить usingдирективу для System.Text.Json. Мы добавляем IJSObjectReferenceполе, представляющее ссылку на модуль JavaScript, и создаем его экземпляр при первом отображении компонента. Мы также добавляем тип, представляющий изображение и ширину изображения. Это объявлено как recordтип с именем ImageDimensions.

В этом HandleChangeслучае мы передаем содержимое загруженного файла как DotNetStreamReferenceмодуль JS. Мы десериализуем возвращаемое значение как экземпляр ImageDimensionsи проверяем, превышает ли ширина 300. Только если это так, мы изменяем размер изображения.

@page "/image-file-tests"
@using System.Text.Json
@inject IJSRuntime JS
@inject HttpClient http
Image File Tests



@code {
   IJSObjectReference module;
   protected override async Task OnAfterRenderAsync(bool firstRender)
   {
       if (firstRender)
       {
           module = await JS.InvokeAsync("import","./js/fileSize.js");
       }
   }

   async Task HandleChange(InputFileChangeEventArgs e)
   {
       var file = e.File;
       var streamRef = new DotNetStreamReference(file.OpenReadStream(file.Size));
       var json = await module.InvokeAsync("getImageDimensions", streamRef);
       var dimensions = JsonSerializer.Deserialize(json);
       if(dimensions.Width > 300)
       {
           file = await file.RequestImageFileAsync(file.ContentType, 300, int.MaxValue);
       }
       var formContent = new MultipartFormDataContent
       {
           { new StreamContent(file.OpenReadStream(file.Size)), "upload", file.Name },
           { new StringContent(file.ContentType), "content-type" }
       };
       await http.PostAsync("/upload", formContent);
   }
     
   record ImageDimensions(int Width, int Height);
}

Резюме

В этом посте я рассмотрел RequestImageFileAsync метод, который используется для изменения размера изображений в приложении Blazor Web Assembly. Я также посмотрел на получение размеров изображения. Я рассматривал возможность использования ImageSharp, кроссплатформенной библиотеки изображений C#, но прислушался к предупреждению проектной группы об использовании этой библиотеки в браузере и в конечном итоге остановился на использовании собственных API-интерфейсов браузера через взаимодействие с JavaScript.