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

2023-11-09

这是【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 博客中,希望自己学习的同时,也可以帮助更多的人。
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

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

  • 【C++]】蓝桥杯练习题 (入门训练 Fibonacci数列)

    img src data image png base64 iVBORw0KGgoAAAANSUhEUgAAA60AAAG8CAYAAAA1sEPIAAAgAElEQVR4Aey9TYudVdb v2x8Awa56QYRRTAgOPBHpS
  • 把eclipse的web项目导入到idea中

    一 导入项目 1 导入 2 module选择eclipse 没有该步骤可以跳过 3 之后一路next即可 二 配置依赖 1 配置依赖和jar包 1 Project 选择相应的sdk 2 Modules 选择导入的模块dataweb 选择De
  • SpringBoot快速实践 --Ⅰ

    文章目录 启动一个SpringBoot项目 如何替换内嵌容器 玩转SpringBoot配置 全局异常处理 过滤器 拦截器 使用Lombok简洁代码 使用IDEA HTTP Client进行接口调试 启动一个SpringBoot项目 如果你觉
  • house of storm

    一 漏洞利用条件 house of storm是一种结合了unsorted bin attack和large bin attack的攻击技术 其基本原理和large bin attack类似 漏洞发生在unsorted bin的chunk放
  • 【C++碎碎念】C++11新特性(声明、智能指针、右值引用、lambda表达式)

    目录 一 新类型 二 统一的初始化 三 声明 四 智能指针 五 右值引用 六 Lambda表达式 一 新类型 C 11新增了long long和unsigned long long 以支持64位 或更宽 的整型 新增了类型char16 t
  • 数据结构 顺序表的定义

    文章目录 1 2 1 顺序表的定义 1 2 2 顺序表上基本操作的实现 1 顺序表的建立 2 顺序表元素的插入 3 顺序表元素的删除 4 顺序表的查找 1 2 1 顺序表的定义 定义 顺序表是用一组地址连续的存储单元依次存储线性表中的数据元
  • flutter滚动到底部_flutter ScrollController如何滚动到底部?

    flutter ScrollController滚动到底部的示例代码如下 方式一 import package flutter scheduler dart import package flutter material dart void
  • 微信小程序开发全流程记录(从前台到后台,到发布)

    微信小程序开发流程记录 一 代码处理 一 微信小程序 前端显示 微信小程序项目的架构 部分特点说明 二 后台服务器 数据交互 需要的环境 特别注意 二 项目部署 一 Wampserver的设置 二 域名的获取 三 小程序官方网站上的设置 一
  • 阿里大数据之路:数据模型篇大总结

    第1章 大数据领域建模综 1 1 为什么需要数据建模 有结构地分类组织和存储是我们面临的一个挑战 数据模型强调从业务 数据存取和使用角度合理存储数据 数据模型方法 以便在性能 成本 效率之间取得最佳平衡 成本 良好的数据模型能极大地减少不必
  • Linux添加yum源,yum下载速度过慢

    CentOS系统更换yum软件安装源 此处以网易为例 第一步 备份你的原镜像文件 以免出错后可以恢复 mv etc yum repos d CentOS Base repo etc yum repos d CentOS Base repo
  • 英伟达新方法入选CVPR 2023:对未知物体的6D姿态追踪和三维重建

    普通手机 随手 拍的雕像 一下就变成了精细的三维重建图 水杯来回动的动态场景下 细节清晰可见 静态场景效果也同样nice 狗狗突出的肋骨都被还原了出来 对比来看其他方法 效果是酱婶的 这就是英伟达最新提出的方法BundleSDF 这是一种可
  • 什么是JVM

    什么是JVM JVM 内存结构 虚拟机的前世今生 从虚拟机的发展到未来的技术发展 未来的Java技术 JVM整体介绍 JVM各版本内存区域的变化 直接内存 深入分析栈和堆 JVM中的对象 JVM中对象的分配 Java中的泛型 垃圾回收算法与
  • PHP发送邮件详细说明

    这两天琢磨了php得原生发送邮件 发现自带得mail方法不太好用 于是上网查询了好多方法 亲测以下方法能用 源代码都在 我的github 到github上下载 https github com PHPMailer PHPMailer htt
  • 2021年“泰迪杯”数据分析技能赛B 题+肥料登记数据分析数据集

    2021 年 泰迪杯 数据分析技能赛 B 题 肥料登记数据分析 一 背景 肥料是农业生产中一种重要的生产资料 其生产销售必须遵循 肥料登记管 理办法 依法在农业行政管理部门进行登记 各省 自治区 直辖市人民政府 农业行政主管部门主要负责本行
  • vue-$nextTick使用详解

    在vue应用中 我们会碰到 nextTick这个东西 偶尔也会使用 多半是与DOM加载相关 不知道 nextTick为何物 这里搜寻了下资料 做一下总结 nextTick说明 在下次Dom更新循环结束之后执行延迟回调 就是说此次数据变化 在
  • Python自学笔记3-数据类型

    Pytho的数值类型包括 name purpose int 整型 long 长整型 Python3中没有 float 浮点数 complex 复数 代码示例 x 3 整数 f 3 141529 浮点数 name Python 字符串 big
  • 【适合一战成硕的你】考研408笔记(计算机网络)王道+天勤(你再也不用做笔记了)拿捏408.

    考研408笔记系列 提示 点击下面的超链接可以直接到达自己想要的专栏 45分 考研408笔记 数据结构 王道 天勤 45分 考研408笔记 计算机组成原理 王道 天勤 35分 考研408笔记 操作系统 王道 天勤 25分 考研408笔记 计

随机推荐

  • Kali下安装 dvwa 的完整详细教程

    kali之DVWA DVWA共有十个模块 分别是 1 Brute Force 暴力 破解 2 Command Injection 命令行注入 3 CSRF 跨站请求伪造 4 File Inclusion 文件包含 5 File Upload
  • 常用的Dos命令与打开cmd的方式

    打开CMD的方式 开始 系统 命令提示符 Win键 R 输入cmd 打开控制台 推荐 在任意的文件夹下面 按住shift键 鼠标右键点击 在此打开命令行窗口 资源管理器的地址栏前面加上cmd 空格 路径 选择以管理员方式运行 常用的Dos命
  • 符合ISO26262标准的建模规范检查模型静态分析静态测试工具

    符合ISO26262标准的建模规范检查模型静态分析静态测试工具 Model Examiner 功能安全解决方案 以下简称MXAM 测试套件是您进行全面静态模型分析的首选工具 MXAM提供了一种简单的方法来检查建模规范 分析模型结构和评估模型
  • GB2312码表

    转载https blog csdn net oshan2012 article details 79070705
  • 【实践】第一个驱动之自动生成主设备号和设备文件

    1 声明两个变量 static struct class firstdrv class static struct class device firstdrv class dev 2 修改函数first drv init void 和fir
  • 解决同时安装搜狗输入法和谷歌输入法后fcitx无法使用

    问题描述 使用搜狗输入法发现老是出现乱码的问题 然后会提示你删除一个搜狗的文件后重启才能正常使用 因此本人后来听了实验室师兄的建议 又直接安装了谷歌输入法 结果刚开始用着正常 后来突然用着界面右上角的fcitx标志直接消失了 输入法也用不了
  • 【Pytorch】卷积神经网络实现手写数字识别

    Pytorch 卷积神经网络实现手写数字识别 1 加载数据 2 模型构建 3 训练模型 4 模型保存 5 模型加载和使用 1 加载数据 分别构建训练集和测试集 验证集 DataLoader来迭代取数据 import torch import
  • DS内排—2-路归并排序

    目录 题目描述 AC代码 题目描述 输入一组字符串 用2 路归并排序按字典顺序进行降序排序 输入 测试次数t 每组测试数据 数据个数n 后跟n个字符串 字符串不含空格 输出 对每组测试数据 输出2 路归并排序的每一趟排序结果 每组测试数据的
  • 红米10A 12.5.12 root 新版本note11 5G 解锁BL BootLoader无法解锁解决方案红米12C 跳过168小时

    红米10A 12 5 12 root 新版本note11 5G 解锁BL BootLoader无法解锁解决方案红米12C 新版本的红米10A dandelion c3l2 images V12 5 10 0 V12 5 11 0 V12 5
  • QPushButton的简单使用

    Qt的基本控件接口 QPushButton的简单使用 Dialog Dialog QWidget parent QDialog parent ui new Ui Dialog ui gt setupUi this QPushButton b
  • 【mcuclub】单片机-STM32F103C8T6

    一 实物图 二 原理图 1 总电源电路 一个type c的插座 一个自锁按键 一个220uF的电解电容 一个1k的限流电阻和一个LED灯 这个220uF的电解电容选取 为什么要 一是电源本身就有纹波 多加一个滤波电容更好 二是电源线有电阻
  • linux下激活窗口 qt_在Linux上通过插件将Qt窗口嵌入到Firefox中

    So this is a trivial example of what I m trying to accomplish Using QX11EmbedContainer and QX11EmbedWidget I can create
  • Linux基础服务5——rsync

    文章目录 一 基本了解 二 rsync的基本操作 2 1 安装rsync 2 2 同步常用参数 2 3 同步目录示例 三 rsync inotify实时同步 3 1 环境准备 3 2 配置服务端 3 3 配置客户端 3 4 自动同步 一 基
  • 主板中的Win10/win8.1 WHQL支持是否要开启

    在新式的电脑主板上会有Windows 10 8 1 WHQL支持开启的选项 这个选项的开启和关闭分别代表什么意义呢 这其实还要从UEFI和Legacy两种不同BIOS的说起 Legacy是传统的BIOS uefi启动是一种新的主板引导项 它
  • Qt框架分析

    以Qt5 6 0为例 类结构 先分析qt gui程序最常用的两个大类QApplication和QWidget的继承关系 如下 在分析QApplication和QWidget的构造过程 如下 结合继承关系和构造过程分析类结构 以QObject
  • 蓝桥杯之python基础

    蓝桥杯之python基础 一 问题与学习 二 python基础的关键知识点 2 1 Python 标识符 2 2 行和缩进 2 3 多行语句 2 4 python引号 2 5 python注释 2 6 python空行 2 7 等待用户输入
  • Postman添加公用url

    1 点击右上角的这个图标 2 点击Add 3 在Add Environment中输入环境名称 下面输入相关参数 4 用两层大括号即可使用 如 http base url login
  • OpenGL纹理映射总结

    大概步骤 1 创建纹理对象 并为他指定一个纹理 2 确定纹理如何应用到每个像素上 3 启用纹理贴图 4 绘制场景 提供纹理和几何坐标 过滤 由于我们提供的纹理图像很少能和最终的屏幕坐标形成对应 大小不同 所以需要设置过滤项目 允许我们进行插
  • cef支持高分辨率去除黑边

    解决方案 1 在当前电脑的桌面 右键 显示设置 把显示比例调整为100 需要重启电脑生效 这时再看 显示就正常了 2 在当前项目中 添加一个 应用程序清单文件 app manifest 在根节点 assembly 下 添加以下代码 重新运行
  • 【Flutter 问题系列第 74 篇】在 Flutter 中如何对 Uint8List 和 File 类型的图像数据进行压缩

    这是 Flutter 问题系列第 74 篇 如果觉得有用的话 欢迎关注专栏 文章目录 一 问题描述 二 引入 获取依赖 三 根据数据类型指定压缩方式 3 1 Uint8List Uint8List 3 2 File File 3 3 Fil