Flutter & Firebase: сжатие перед загрузкой изображения

Я хочу отправить фотографию, выбранную пользователем в моем приложении, в Firebase Storage. У меня есть простой класс с свойством _imageFile который устанавливается следующим образом:

File _imageFile;

_getImage() async {
    var fileName = await ImagePicker.pickImage();
    setState(() {
        _imageFile = fileName;
    });
}

после этого отправлю фото как с помощью этого кода:

final String rand1 = "${new Random().nextInt(10000)}";
final String rand2 = "${new Random().nextInt(10000)}";
final String rand3 = "${new Random().nextInt(10000)}";
final StorageReference ref = FirebaseStorage.instance.ref().child('${rand1}_${rand2}_${rand3}.jpg');
final StorageUploadTask uploadTask = ref.put(_imageFile);
final Uri downloadUrl = (await uploadTask.future).downloadUrl;
print(downloadUrl);

Проблема в том, что фотографии часто очень большие. Есть ли какой-либо метод в Flutter/Dart для сжатия и изменения размера фотографии перед загрузкой? Я в порядке с потерей качества.

Ответ 1

Плагин image_picker в настоящее время очень прост. Было бы просто добавить опцию для указания желаемого размера/качества выбранного изображения. Если вы сделаете это, пожалуйста, отправьте нам запрос!

Ответ 2

Я столкнулся с этим и смог выполнить сжатие/изменение размера с помощью пакета изображений Dart вместе с поставщиком путей. Вы можете посмотреть анимированные изображения и примеры для других способов и помощи.

Вот что я сделал:

import 'package:image/image.dart' as Im;
import 'package:path_provider/path_provider.dart';
import 'dart:math' as Math;

void compressImage() async {
  File imageFile = await ImagePicker.pickImage();
  final tempDir = await getTemporaryDirectory();
  final path = tempDir.path;
  int rand = new Math.Random().nextInt(10000);

  Im.Image image = Im.decodeImage(imageFile.readAsBytesSync());
  Im.Image smallerImage = Im.copyResize(image, 500); // choose the size here, it will maintain aspect ratio

  var compressedImage = new File('$path/img_$rand.jpg')..writeAsBytesSync(Im.encodeJpg(image, quality: 85));
}

Затем я загрузил compressedImage в хранилище firebase. Вы можете настроить качество сохранения jpg с использованием свойства качества, в моем случае я выбрал 85 (из 100).

Надеюсь это поможет! Дайте знать, если у вас появятся вопросы.

Ответ 3

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

import 'dart:async' show Future;
import 'dart:io' show File;
import 'package:flutter/foundation.dart' show compute;
import 'package:flutter/material.dart' show BuildContext;
import 'package:image/image.dart' as Im;
import 'dart:math' as Math;
import 'package:image_picker/image_picker.dart';
import 'package:path_provider/path_provider.dart' show getTemporaryDirectory;

Future<File> takeCompressedPicture(BuildContext context) async {
  var _imageFile = await ImagePicker.pickImage(source: ImageSource.camera);
  if (_imageFile == null) {
    return null;
  }

  // You can have a loading dialog here but don't forget to pop before return file;

  final tempDir = await getTemporaryDirectory();
  final rand = Math.Random().nextInt(10000);
  _CompressObject compressObject =
      _CompressObject(_imageFile, tempDir.path, rand);
  String filePath = await _compressImage(compressObject);
  print('new path: ' + filePath);
  File file = File(filePath);

  // Pop loading

  return file;
}

Future<String> _compressImage(_CompressObject object) async {
  return compute(_decodeImage, object);
}

String _decodeImage(_CompressObject object) {
  Im.Image image = Im.decodeImage(object.imageFile.readAsBytesSync());
  Im.Image smallerImage = Im.copyResize(
      image, 1024); // choose the size here, it will maintain aspect ratio
  var decodedImageFile = File(object.path + '/img_${object.rand}.jpg');
  decodedImageFile.writeAsBytesSync(Im.encodeJpg(smallerImage, quality: 85));
  return decodedImageFile.path;
}

class _CompressObject {
  File imageFile;
  String path;
  int rand;

  _CompressObject(this.imageFile, this.path, this.rand);
}

Вы можете назвать это очень легко с этим:

import 'path/to/compress_image.dart' as CompressImage;
// ...
File file = await CompressImage.takeCompressedPicture(context);

Ответ 4

Помимо упоминания этой нативной библиотеки: https://pub.dartlang.org/packages/flutter_image_compress

Это полностью основанный на дротике компрессор с изоляторами, который может сделать сжатие параллельным потоку пользовательского интерфейса в многоядерных процессорах.

Возможно, вы захотите использовать функцию вычисления, которая упрощает использование изолятов: https://docs.flutter.io/flutter/foundation/compute.html https://flutter.io/cookbook/networking/background-parsing/

import 'package:image/image.dart' as ImageLib;
import 'package:path_provider/path_provider.dart';

Future<void> getCompressedImage(SendPort sendPort) async {
  ReceivePort receivePort = ReceivePort();

  sendPort.send(receivePort.sendPort);
  List msg = (await receivePort.first) as List;

  String srcPath = msg[0];
  String name = msg[1];
  String destDirPath = msg[2];
  SendPort replyPort = msg[3];

  ImageLib.Image image =
      ImageLib.decodeImage(await new File(srcPath).readAsBytes());

  if (image.width > 500 || image.height > 500) {
    image = ImageLib.copyResize(image, 500);
  }

  File destFile = new File(destDirPath + '/' + name);
  await destFile.writeAsBytes(ImageLib.encodeJpg(image, quality: 60));

  replyPort.send(destFile.path);
}

Future<File> compressImage(File f) async {
  ReceivePort receivePort = ReceivePort();

  await Isolate.spawn(getCompressedImage, receivePort.sendPort);
  SendPort sendPort = await receivePort.first;

  ReceivePort receivePort2 = ReceivePort();

  sendPort.send([
    f.path,
    f.uri.pathSegments.last,
    (await getTemporaryDirectory()).path,
    receivePort2.sendPort,
  ]);

  var msg = await receivePort2.first;

  return new File(msg);
}

if (false ==
    await SimplePermissions.checkPermission(
        Permission.ReadExternalStorage)) {
  await SimplePermissions.requestPermission(
    Permission.ReadExternalStorage);
}

File img = await ImagePicker.pickImage(
    source: ImageSource.gallery);
if (null != img) {
  img = await compressImage(img);
}

Ответ 5

Используйте плагин image_picker и вызовите функцию изображения как

Future<File> imageFile = ImagePicker.pickImage(source: ImageSource.gallery, maxHeight: 200, maxWidth: 200 );

измените maxHeight и maxWidth на любой размер изображения, который вам нужен.

Ответ 6

Пока я использую

пакет: изображение /image.dart

столкнувшись с нижеуказанными вопросами

  • показ пустой страницы при преобразовании изображения (может потребоваться так много времени при сжатии изображения)
  • После сжатия изображение растянулось и не выглядело хорошо

Затем использовал плагин ниже, тоже работает нормально, без проблем, даже быстрее и что я ожидал

https://github.com/btastic/flutter_native_image.git

шаги и метод доступны по ссылке выше.