Как я могу конвертировать туда и обратно между большим двоичным объектом и изображением в Flutter Web?

#flutter #dart #flutter-web #dart-html

#flutter #dart #flutter-web #dart-html

Вопрос:

Контекст

Я использую image_picker с Flutter web, чтобы позволить пользователям выбирать изображение. Это возвращает URI объекта Blob-объекта локальной сети, с помощью которого я могу отображать Image.network(pickedFile.path) . У меня возникают проблемы, когда я хочу начать манипулировать этим изображением. Сначала мне нужно извлечь его из сети в память. Когда я закончу, мне нужно вернуть его обратно в большой двоичный объект, доступный по сети.

Как мне создать большой двоичный объект из изображения?

Я не имею в виду встроенный виджет изображения. Я имею в виду ImageLib.Изображение, где ImageLib — это библиотека изображений Dart. Почему я хочу это сделать? Ну, у меня есть веб-приложение, в котором пользователь выбирает изображение, которое возвращается как большой двоичный объект. Я переношу это в память, использую ImageLib для обрезки и изменения размера, а затем хочу вернуть его обратно в URL-адрес большого двоичного объекта. Вот где сейчас находится мой код:

 # BROKEN:
var png = ImageLib.encodePng(croppedImage);
var blob = html.Blob([base64Encode(png)], 'image/png');
var url = html.Url.createObjectUrl(blob);
  

Код не выдает ошибку, пока я не попытаюсь отобразить изображение с Image(image: NetworkImage(url)) помощью . Ошибка начинается с:

Было вызвано следующее событие $ object, разрешающее кадр изображения:

Копирование и вставка url в браузер показывает черный экран, который я принимаю за изображение 0x0. И вот я перехожу к своим вопросам:

  1. Как мне правильно закодировать изображение и создать большой двоичный объект?
  2. Есть ли лучший способ манипулировать изображениями в Flutter web помимо использования больших двоичных объектов? Я в основном использую его только потому, что это то, что возвращает image_picker_for_web, и поэтому это единственный метод, который я знаю, кроме возможного использования виртуальной файловой системы, которую я не слишком изучал.

Как мне извлечь изображение в память?

Пока я этим занимаюсь, я мог бы также спросить, какова наилучшая практика для переноса изображения в память. Для мобильных image_picker устройств я получал имя файла, и я использовал бы package:image/image.dart as ImageLib для управления им:

 // pickedfile.path is the name of a file
ImageLib.Image img = ImageLib.decodeImage(File(pickedfile.path).readAsBytesSync());
  

С web у меня нет доступа к файловой системе, поэтому я делаю это вместо этого:

 // pickedfile.path is the URL of an HTML Blob
var response = await http.get(pickedfile.path);
ImageLib.Image img = ImageLib.decodeImage(response.bodyBytes);
  

Это значительно медленнее, чем старый способ, вероятно, из-за GET. Действительно ли это лучший (или единственный) способ сохранить мое изображение в памяти?

Комментарии:

1. у вас такая же проблема, вы решили это?

2. Смотрите мой ответ ниже.

Ответ №1:

Секрет, как предложил Брендан Дункан, заключался в использовании встроенных функций декодирования браузера:

   // user browser to decode
  html.ImageElement myImageElement = html.ImageElement(src: imagePath);
  await myImageElement.onLoad.first; // allow time for browser to render
  html.CanvasElement myCanvas = html.CanvasElement(width: myImageElement.width, height: myImageElement.height);
  html.CanvasRenderingContext2D ctx = myCanvas.context2D;

  //ctx.drawImage(myImageElement, 0, 0);
  //html.ImageData rgbaData = ctx.getImageData(0, 0, myImageElement.width, myImageElement.height);

  // resize to save time on encoding
  int _MAXDIM = 500;
  int width, height;
  if (myImageElement.width > myImageElement.height) {
    width = _MAXDIM;
    height = (_MAXDIM*myImageElement.height/ myImageElement.width).round();
  } else {
    height = _MAXDIM;
    width = (_MAXDIM*myImageElement.width/ myImageElement.height).round();
  }
  ctx.drawImageScaled(myImageElement, 0, 0, width, height);
  html.ImageData rgbaData = ctx.getImageData(0, 0, width, height);

  var myImage = ImageLib.Image.fromBytes(rgbaData.width, rgbaData.height, rgbaData.data);
  

Он предложил аналогичный трюк для кодирования, но для моего варианта использования было достаточно сделать это с помощью Dart:

   int width, height;
  if (myImageElement.width > myImageElement.height) {
    width = 800;
    height = (800*myImageElement.height/ myImageElement.width).round();
  } else {
    height = 800;
    width = (800*myImageElement.width/ myImageElement.height).round();
  }
  ctx.drawImageScaled(myImageElement, 0, 0, width, height);
  html.ImageData rgbaData = ctx.getImageData(0, 0, width, height);
  var myImage = ImageLib.Image.fromBytes(rgbaData.width, rgbaData.height, rgbaData.data);
  

Обратите внимание, что в обоих случаях я сначала изменяю размер изображения, чтобы уменьшить его размер.

Комментарии:

1. как я могу отправить его на сервер Multipart с помощью dio?

2. Я загружаю в Firebase. Сначала я кодирую изображение, как указано выше, а затем: « // кодирую как строку jpg ContentType = ‘image / jpeg’; var jpg = ImageLib.encodeJpg(img, качество: 90); // загрузить в URL-адрес строки хранилища firebase; попробуйте { fb.StorageReference _storage = fb.storage().ref(imageKey); fb.UploadTaskSnapshot uploadTaskSnapshot = await _storage.put(jpg, fb.UploadMetadata(ContentType: ContentType)). future; var imageUri = ожидание загрузки tasksnapshot.ref.getDownloadURL(); url = imageUri.toString(); } catch (e) { print(e); } «

3. Было бы действительно здорово, если бы вы могли предложить полное решение, оно разреженное и сложное для понимания. Что касается меня, я не могу использовать ваше решение для загрузки изображения большого двоичного объекта в память.