Flutter & Firebase:上传图片前的压缩

Posted

技术标签:

【中文标题】Flutter & Firebase:上传图片前的压缩【英文标题】:Flutter & Firebase: Compression before upload image 【发布时间】:2018-03-12 22:39:31 【问题描述】:

我想将用户在我的应用中选择的照片发送到 Firebase 存储。我有一个简单的类,其属性为_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 中是否有任何方法可以在上传前压缩和调整照片大小?我可以接受质量损失。

【问题讨论】:

你可能想试试pub.dartlang.org/packages/image,这个我没试过,但是当你阅读文档你会发现你可以将图像编码成不同的格式并指定不同的压缩级别。跨度> 您可能需要检查 pub.dev/packages/uuid 是否在 firebase 上上传文件,因为使用随机数有时可能会发生冲突。 如果您喜欢 lutter web,请查看我的回答。 ***.com/questions/60728872/… 【参考方案1】:

好吧1。如果您使用移动设备,则可以使用 flutter_image_compress: ^1.0.0 来完成这项工作

例如。 通过你的 Uint8List 和质量,你很快就会得到压缩图像。

Future<Uint8List> testComporessList(Uint8List uint8List) async 
    var result = await FlutterImageCompress.compressWithList(
      uint8List,
      quality: 50,
    );
    return result;

2。但是,如果您在 Flutter Web 中,那么除了图像选择器等之外,您将没有其他选择。 我最终使用了javascript。你可以在这里找到答案。Flutter Web: How Do You Compress an Image/File?

【讨论】:

【参考方案2】:

你可以这样做,

//Compressing Image
    File compressedImg = await FlutterNativeImage.compressImage(
      _image.path,
      quality: 70,
    );
//Compressing Image

【讨论】:

【参考方案3】:

有很多解决方案:

使用image_picker包:

您可以使用 ImagePicker 的内置 imageQuality 属性来压缩图像。该属性取一个介于0100 之间的值,表示原始图像质量的百分比。

首先,在您的 pubspec.yaml 文件中添加 image_picker 作为依赖项。

用法

  File _image;

  Future getImage() async 
    var image = await ImagePicker.pickImage(
        source: ImageSource.gallery,  
                imageQuality: 25,
    );

    setState(() 
      _image = image;
    );
  

这种方法的优点在于它嵌入在 image_picker 包,因此非常易于使用。

您还可以从图像小部件调整质量。

使用filterQuality设置图片的FilterQuality

示例:

Image.asset('assets/kab1.png', filterQuality: FilterQuality.high,), 

这些属性存在于 AssetImageNetworkImageFileImageMemoryImage 中。

您也可以简单地调整图像大小(调整图像大小可以压缩它)。为此,请尝试使用 ResizeImage 小部件

另一种解决方案是使用flutter_image_compress 包

【讨论】:

【参考方案4】:

2020 年 6 月 5 日 - 更新

image_picker 插件现在支持imageQuality 参数。你可以做类似的事情

ImagePicker imagePicker = ImagePicker();
PickedFile compressedImage = await imagePicker.getImage(
  source: ImageSource.camera,
  imageQuality: 85,
);

旧答案

或者如果您想使用ImagePicker压缩图像

我遇到了这个问题,并且能够使用 Dart image package 和 path provider 完成压缩/调整大小。您可以查看 dart image api 和 examples 以获得其他方式和更多帮助。

这就是我所做的:

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 存储。您可以使用 quality 属性调整保存 jpg 的质量,在我的例子中,我选择了 85(满分 100)。

希望这会有所帮助!如果您有任何问题,请告诉我。

【讨论】:

我也在做同样的事情,但在 ios 中需要很长时间(如果图像是从中获取的) 是的,它很长,我无法理解为什么它会冻结我的 UI,即使代码是异步的 我发现它在调试时确实冻结了我的用户界面一段时间,当应用程序为发布而构建时它运行得更好 @gbaccetta 是异步的并不意味着它是并行的 @Mans 平均图像文件大小减少多少?【参考方案5】:

2020 年更新“pickImage”已弃用,不应使用。改用 imagePicker.getImage() 方法**

  ImagePicker picker = ImagePicker();
   PickedFile compressedImage = await imagePicker.getImage(
  source: ImageSource.camera,
  imageQuality: 80,
);

图像质量文档:

返回一个 PickedFile 对象,该对象包装被拾取的图像。这 返回的 PickedFile 旨在在单个 APP 中使用 会议。不要保存文件路径并跨会话使用它。这 source 参数控制图像的来源。这可以是 ImageSource.camera 或 ImageSource.gallery。 iOS 支持的地方 HEIC 图像,android 8 及更低版本没有。仅限 Android 9 及更高版本 支持 HEIC 图像,如果除了尺寸修改之外还使用, 下面解释其用法。如果指定,图像将位于 最大 maxWidth 宽和 maxHeight 高。否则图像会 以原来的宽度和高度返回。 imageQuality 参数 修改图像的质量,范围为 0-100,其中 100 是 原始/最高质量。如果 imageQuality 为空,则带有 原始质量将被退回。仅支持压缩 某些图像类型,例如 JPEG 以及 Android PNG 和 WebP。如果 选择的图像不支持压缩,警告 消息将被记录。使用 preferredCameraDevice 指定 当源是 ImageSource.camera 时使用的相机。这 当源是 ImageSource.gallery 时,preferredCameraDevice 将被忽略。 如果所选相机不支持,它也会被忽略 设备。默认为 CameraDevice.rear。请注意,Android 没有 用于指定是前还是后的意图的记录参数 相机应该打开,这个功能不能保证在一个 安卓设备。在 Android 中,MainActivity 可以被销毁 很多原因。如果发生这种情况,结果将丢失在此 称呼。然后,您可以在您的应用重新启动时调用 getLostData 找回丢失的数据。

【讨论】:

【参考方案6】:

由于您使用的是 Firebase,因此一种选择是使用扩展程序 - 调整图像大小。它使您可以选择保留或删除原始图像,并且非常易于安装和使用。

【讨论】:

点评来源: 这篇文章似乎没有为问题提供quality answer。请编辑您的答案,或将其作为对问题的评论发布。【参考方案7】:

您可以使用名为flutter_image_compress的插件

// Active image file
File _imageFile;

// Select an image via gallery or camera
Future<void> _pickImage(ImageSource source) async 
  File selected = await ImagePicker.pickImage(source: source);

// Compress plugin
  File compressedImage = await FlutterImageCompress.compressAndGetFile(
    selected.path,
    selected.path,
    quality: 50,
  );

  setState(() 
    _imageFile = compressedImage;
    print('compressedimagesize: $_imageFile.lengthSync()');
  );

瞧!压缩文件图片

【讨论】:

表示目标路径不能与文件路径相同。【参考方案8】:

当我使用时

package:image/image.dart

面临以下问题

在转换图像时显示空白页(压缩图像时可能需要进行很多处理) 压缩后的图像被拉伸,看起来不太好

然后在插件下面使用,同样工作正常,没有任何问题,甚至更快,符合我的预期

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

以上链接中提供的步骤和方法。

【讨论】:

【参考方案9】:

使用image_picker插件并调用pick image函数

Future&lt;File&gt; imageFile = ImagePicker.pickImage(source: ImageSource.gallery , maxHeight: 200 , maxWidth: 200 );

将 maxHeight 和 maxWidth 更改为您需要的任何大小的图像。

【讨论】:

这对我有用!我使用 400 而不是 200 来获得更好的质量。一般一个200x200小于20kb,400x400小于40kb 太好了,这是我需要的。我不想使用另一个库进行压缩。 2020 : 'pickImage' 已弃用,不应使用。改用 imagePicker.getImage() 方法..【参考方案10】:

以下代码是我用相机拍摄图像然后压缩它的代码:

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);

【讨论】:

return compute(_decodeImage, object); 提供此错误,例如“无效参数:隔离消息中的非法参数:(对象是一个闭包 - 函数 '_decodeImage@38216194':.)'【参考方案11】:

除了提到这个 native 库之外: https://pub.dartlang.org/packages/flutter_image_compress

这是一个完全基于 Dart 的带有隔离的压缩器,它可以使压缩与多核 CPU 中的 UI 线程并行。

您可能想要使用计算函数,这使得使用隔离更简单: 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);

【讨论】:

有时会挂断 :( 无一例外 忘记import 'dart:isolate';【参考方案12】:

image_picker 插件目前非常简单。添加一个选项来指定拾取图像的所需大小/质量是很简单的。如果您这样做,请向我们发送拉取请求!

【讨论】:

图片选择器库中要传入什么参数?你能提供它的链接吗? 我没有找到任何参数来调整 X 和 Y 以上所有图像的大小,你找到了吗?谢谢 在图像选择器库中使用 imageQuality 参数。 语法:ImagePicker.pickImage(source.ImageSource.gallery, imageQuality: 85);但这会将图像压缩为其当前质量的 85%,因此,如果图像的质量已经较低,那么它只会更加混乱!替代解决方案:ImagePicker.pickImage(source.ImageSource.gallery, maxHeight: 200);使用 maxHeight 只会在图像大于提到的高度时压缩图像! 网络有什么解决办法吗?

以上是关于Flutter & Firebase:上传图片前的压缩的主要内容,如果未能解决你的问题,请参考以下文章

Flutter:获取 Firebase 存储下载 URL 和上传状态 [重复]

Flutter - 录制语音并将其上传到 firebase

Flutter - 将图像上传到 Firebase 存储

Flutter - 将图像上传到 Firebase 存储

Flutter-Firebase 存储上传视频

Flutter:如何将pdf文件上传到firebase存储?