【问题标题】:Flutter & Firebase: Compression before upload imageFlutter & Firebase:上传图片前的压缩
【发布时间】: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 中是否有任何方法可以在上传前压缩和调整照片大小?我可以接受质量损失。

【问题讨论】:

标签: firebase dart firebase-storage flutter


【解决方案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 packagepath provider 完成压缩/调整大小。您可以查看 dart image apiexamples 以获得其他方式和更多帮助。

        这就是我所做的:

        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 只会在图像大于提到的高度时压缩图像!
            • 网络有什么解决办法吗?
            猜你喜欢
            • 1970-01-01
            • 2021-01-03
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2020-08-14
            • 2015-09-02
            • 2013-11-04
            • 1970-01-01
            相关资源
            最近更新 更多