【Flutter 问题系列第 74 篇】在 Flutter 中如何对 Uint8List 和 File 类型的图像数据进行压缩

这是【Flutter 问题系列第 74 篇】,如果觉得有用的话,欢迎关注专栏。

一:问题描述

项目中用到了百度 OCR 图像识别的功能,如果上传的图片大于 4 兆的话,就会提示因图片过大识别失败。

所以需要对上传的图像进行压缩,在 Flutter 中如何对图像数据进行压缩呢?

二:引入,获取依赖

这里用到了 pub 上的第三方插件库 flutter_image_compress,同时支持在 Android 和 iOS 上运行。截止到发文最新版本是 1.1.0 。

在项目的配置文件 pubspec.yaml 中引入依赖,如下图所示

在这里插入图片描述

然后在终端执行 flutter pub get 命令获取依赖。

三:根据数据类型指定压缩方式

这里我根据官方文档指定的数据类型,封装了四种不同的图像数据转换的方法,

这里自定义一个压缩图片的工具类 CompressUtil,并指定其最小分辨率的高度和宽度。

如下代码所示

class CompressUtil {
    
    
  static int minHeight = 1920; // 指定最小分辨率的高度
  static int minWidth = 1080; // 指定最小分辨率的宽度
}

然后在当前工具类中,定义了如下四个压缩的方法,简单明了,不需要你对插件有了解,直接拿走使用就行。

3-1:Uint8List → Uint8List

第一种压缩方式的参数类型是 Uint8List ,返回值类型是 Future<Uint8List>,如下代码所示

  /// 压缩方式一 Uint8List -> Uint8List
  static Future<Uint8List> u8ToU8(Uint8List list) async {
    
    
    int quality = imageQuality(list.length);
    Uint8List result = await FlutterImageCompress.compressWithList(
      list,
      minWidth: minWidth,
      minHeight: minHeight,
      quality: quality,
    );
    debugPrint("压缩后图片的大小:${size(result.length)}");
    return result;
  }

3-2:File → File

第二种压缩方式的参数类型是 File,返回值类型是 Future<File?>,如下代码所示

  /// 压缩方式二 File -> File
  static Future<File?> fileToFile(File file) async {
    
    
    // 图片质量
    int quality = imageQuality(file.readAsBytesSync().length);
    // 缓存路径
    Directory cache = await getTemporaryDirectory();
    int time = DateTime.now().millisecondsSinceEpoch;
    String savePath = cache.path + "/AllenSu_$time.jpg"; // 指定压缩后图片的路径
    File? result = await FlutterImageCompress.compressAndGetFile(
      file.path,
      savePath,
      minWidth: minWidth,
      minHeight: minHeight,
      quality: quality,
    );
    if (result != null) {
    
    
      debugPrint("压缩后图片的大小:${size(result.readAsBytesSync().length)}");
    }
    return result;
  }

获取文件路径这里用到了插件 path_provider,需要在 pubspec.yaml 文件中引入。

压缩后图片的路径你可以修改为自定义的路径。

3-3:File → Uint8List

第三种压缩方式的参数类型是 File,返回值类型是 Future<Uint8List?>,如下代码所示

  /// 压缩方式三 File -> Uint8List
  static Future<Uint8List?> fileToU8(File file) async {
    
    
    // 图片质量
    int quality = imageQuality(file.readAsBytesSync().length);
    Uint8List? result = await FlutterImageCompress.compressWithFile(
      file.path,
      minWidth: minWidth,
      minHeight: minHeight,
      quality: quality,
    );
    if (result != null) {
    
    
      debugPrint("压缩后图片的大小:${size(result.length)}");
    }
    return result;
  }

3-4:Asset → Uint8List

第三种压缩方式的参数类型是 String(其实就是图片的路径 path),返回值类型是 Future<Uint8List?>,如下代码所示

  /// 压缩方式四 Asset -> Uint8List
  static Future<Uint8List?> assetToU8(String assetName) async {
    
    
    File file = File(assetName);
    // 图片质量
    int quality = imageQuality(file.readAsBytesSync().length);
    Uint8List? result = await FlutterImageCompress.compressAssetImage(
      assetName,
      minWidth: minWidth,
      minHeight: minHeight,
      quality: quality,
    );
    if (result != null) {
    
    
      debugPrint("压缩后图片的大小:${size(result.length)}");
    }
    return result!;
  }

以上四种压缩图片的方式满足正常开发是没有问题的。

解释说明

代码中的方法 imageQuality(int length)size((int length)) 是我自己扩展的,原因如下

  • 对于方法 imageQuality,是为了根据传入的图片字节长度,返回指定的图片质量。如果你想固定压缩图片的质量,可以不用在意这个方法。
  • 对于方法 size,是为了方便查看图片的大小,因为图片的大小是字节数组的长度,看起来不太方便,所以进行了转换,如果图片小于 1 兆,则显示 KB,如果图片大于 1 兆,则显示 MB,并保留一位小数。

我并没有把代码中的注释去掉,因为我觉得你在调试的时候应该可以用得到。

四:完整代码

以上是对封装的工具类进行了拆分,对工具类的每个方法进行了解释说明。

但为方便大家复制,最后把完整的代码贴到这里,如下

import 'dart:io';
import 'dart:typed_data';
import 'package:flutter/widgets.dart';
import 'package:flutter_image_compress/flutter_image_compress.dart';
import 'package:path_provider/path_provider.dart';

// ------------------------------------------------------
// author:Allen Su
// date  :2022/7/9 00:08
// usage :图片压缩工具类
// ------------------------------------------------------

class CompressUtil {
    
    
  static int minHeight = 1920; // 指定最小分辨率的高度
  static int minWidth = 1080; // 指定最小分辨率的宽度

  /// 压缩方式一 Uint8List -> Uint8List
  static Future<Uint8List> u8ToU8(Uint8List list) async {
    
    
    int quality = imageQuality(list.length);
    Uint8List result = await FlutterImageCompress.compressWithList(
      list,
      minWidth: minWidth,
      minHeight: minHeight,
      quality: quality,
    );
    debugPrint("压缩后图片的大小:${size(result.length)}");
    return result;
  }

  /// 压缩方式二 File -> File
  static Future<File?> fileToFile(File file) async {
    
    
    // 图片质量
    int quality = imageQuality(file.readAsBytesSync().length);
    // 缓存路径
    Directory cache = await getTemporaryDirectory();
    int time = DateTime.now().millisecondsSinceEpoch;
    String savePath = cache.path + "/AllenSu_$time.jpg";
    File? result = await FlutterImageCompress.compressAndGetFile(
      file.path,
      savePath,
      minWidth: minWidth,
      minHeight: minHeight,
      quality: quality,
    );
    if (result != null) {
    
    
      debugPrint("压缩后图片的大小:${size(result.readAsBytesSync().length)}");
    }
    return result;
  }

  /// 压缩方式三 File -> Uint8List
  static Future<Uint8List?> fileToU8(File file) async {
    
    
    // 图片质量
    int quality = imageQuality(file.readAsBytesSync().length);
    Uint8List? result = await FlutterImageCompress.compressWithFile(
      file.path,
      minWidth: minWidth,
      minHeight: minHeight,
      quality: quality,
    );
    if (result != null) {
    
    
      debugPrint("压缩后图片的大小:${size(result.length)}");
    }
    return result;
  }

  /// 压缩方式四 Asset -> Uint8List
  static Future<Uint8List?> assetToU8(String assetName) async {
    
    
    File file = File(assetName);
    // 图片质量
    int quality = imageQuality(file.readAsBytesSync().length);
    Uint8List? result = await FlutterImageCompress.compressAssetImage(
      assetName,
      minWidth: minWidth,
      minHeight: minHeight,
      quality: quality,
    );
    if (result != null) {
    
    
      debugPrint("压缩后图片的大小:${size(result.length)}");
    }
    return result!;
  }

  /// 根据传入的图片字节长度,返回指定的图片质量
  static int imageQuality(int length) {
    
    
    debugPrint("压缩前图片的大小:${size(length)}");
    int quality = 100; // 图片质量指数
    int m = 1024 * 1024; // 1 兆
    if (length < 0.5 * m) {
    
    
      quality = 70;
      debugPrint("图片小于 0.5 兆,质量设置为 70");
    } else if (length >= 0.5 * m && length < 1 * m) {
    
    
      quality = 60;
      debugPrint("图片大于 0.5 兆小于 1 兆,质量设置为 60");
    } else if (length >= 1 * m && length < 2 * m) {
    
    
      quality = 50;
      debugPrint("图片大于 1 兆小于 2 兆,质量设置为 50");
    } else if (length >= 2 * m && length < 3 * m) {
    
    
      quality = 40;
      debugPrint("图片大于 2 兆小于 3 兆,质量设置为 40");
    } else if (length >= 3 * m && length < 4 * m) {
    
    
      quality = 30;
      debugPrint("图片大于 3 兆小于 4 兆,质量设置为 30");
    } else {
    
    
      quality = 20;
      debugPrint("图片大于 4 兆,质量设置为 20");
    }
    return quality;
  }

  /// 根据传入的字节长度,转换成相应的 M 和 KB
  static String size(int length) {
    
    
    String res = "";
    final int unit = 1024;
    final int m = unit * unit; // 1M
    // 如果小于 1 兆,显示 KB
    if (length < 1 * m) {
    
    
      res = (length / unit).toStringAsFixed(0) + " KB";
    }
    // 如果大于 1 兆,显示 MB,并保留一位小数
    if (length >= 1 * m) {
    
    
      res = (length / m).toStringAsFixed(1) + " MB";
    }
    return res;
  }
}

经测试,1 张 1.2 兆的图片,如果设置压缩质量为 50,压缩后大概在 50 - 100 kb 之间,图片虽然小了许多,但还是很清晰的,且 OCR 识别成功率几乎是 100 %。

至此,关于如何在 Flutter 中对图像数据进行压缩便介绍到这里。

你的问题得到解决了吗?欢迎在评论区留言。

赠人玫瑰,手有余香,如果觉得文章不错,希望可以给个一键三连,感谢。


结束语

Google 的 Flutter 越来越火,截止 2022年7月9日 GitHub 标星已达 142K,Flutter 毅然是一种趋势,所以作为前端开发者,没有理由不趁早去学习。

无论你是 Flutter 新手还是已经入门了,不妨先点个关注,后续我会将 Flutter 中的常用组件(含有源码分析、组件的用法及注意事项)以及可能遇到的问题写到 CSDN 博客中,希望自己学习的同时,也可以帮助更多的人。

猜你喜欢

转载自blog.csdn.net/qq_42351033/article/details/125685323