「这是我参与11月更文挑战的第28天,活动详情查看:2021最后一次更文挑战」。
本文翻译自 flutter_image_compress | Flutter Package (pub.dev)
压缩图片的原生插件(Obj-C/Kotlin)
该库可以在 Android 和 iOS 上运行
为什么要使用 Dart 做这个?
Q:Dart 已经有了图片压缩库,为什么要做一个原生的? A:因为一些未知的原因,Dart 语言中的图片压缩效率不高,尤其是在发布版中。使用 isoalte 没有解决这个问题。
1.0.0
1.0.0 是空安全版本。
使用
dependencies:
flutter_image_compress: ^1.0.0-nullsafety
import 'package:flutter_image_compress/flutter_image_compress.dart';
用法参考:完整示例
这里有一些使用该库 api 的方式:
// 1. 压缩文件,返回Uint8List 。
Future<Uint8List> testCompressFile(File file) async {
var result = await FlutterImageCompress.compressWithFile(
file.absolute.path,
minWidth: 2300,
minHeight: 1500,
quality: 94,
rotate: 90,
);
print(file.lengthSync());
print(result.length);
return result;
}
// 2. 压缩文件,返回文件 。
Future<File> testCompressAndGetFile(File file, String targetPath) async {
var result = await FlutterImageCompress.compressAndGetFile(
file.absolute.path, targetPath,
quality: 88,
rotate: 180,
);
print(file.lengthSync());
print(result.lengthSync());
return result;
}
// 3. 压缩 asset,返回 Uint8List 。
Future<Uint8List> testCompressAsset(String assetName) async {
var list = await FlutterImageCompress.compressAssetImage(
assetName,
minHeight: 1920,
minWidth: 1080,
quality: 96,
rotate: 180,
);
return list;
}
// 4. 压缩 Uint8List ,返回另外一个 Uint8List 。
Future<Uint8List> testComporessList(Uint8List list) async {
var result = await FlutterImageCompress.compressWithList(
list,
minHeight: 1920,
minWidth: 1080,
quality: 96,
rotate: 135,
);
print(list.length);
print(result.length);
return result;
}
关于共同参数
最小宽度和最小高度
minWidth 和 minHeight 用来约束图片的缩放。
例如:一个 4000 * 2000 的图片,minWidth 设为 1920, minHeight 设为 1080 ,计算会如下:
// 这里使用 dart 作为示例,真正的实现是 Kotlin 或 OC。
import 'dart:math' as math;
void main() {
var scale = calcScale(
srcWidth: 4000,
srcHeight: 2000,
minWidth: 1920,
minHeight: 1080,
);
print("scale = $scale"); // 缩放比率 = 1.8518518518518519
print("target width = ${4000 / scale}, height = ${2000 / scale}"); // 目标宽度 = 2160.0, height = 1080.0
}
double calcScale({
double srcWidth,
double srcHeight,
double minWidth,
double minHeight,
}) {
var scaleW = srcWidth / minWidth;
var scaleH = srcHeight / minHeight;
var scale = math.max(1.0, math.min(scaleW, scaleH));
return scale;
}
如果你的图片宽度小于最小宽度、高度小于最小高度,scale (缩放比)会是1,也就是大小不会变化。
rotate (旋转)
如果需要旋转图片,使用这个参数。
autoCorrectionAngle (自动矫正角度)
这个参数只在 0.5.0 版本后存在。
由于历史原因,这里可能会和 rotate 属性有冲突,需要自我修正。 将 rotate 改为 0 或将 autoCorrectionAngle 设为 false 。
quality (质量)
目标图片的质量。
如果 format 是 png,在 iOS 上会被忽略。
format
支持 jpeg 或 png ,默认是 jpeg 。
格式类标记为 enum CompressFormat。
支持部分 Heif 和 webp 。
Webp
使用系统 api (速度很好)支持 Android。
尽管也支持 iOS,但是没有系统的实现,使用了第三方库,因为编码速度所以不建议使用。 将来,google 的 libwebp(c/c++) 可能会通过其它第三方库用来做编码工作,但是现在不能保证实现时间。
HEIF(Heic)
Heif for iOS
Only support iOS 11+.
Heif for Android
使用 HeifWriter 实现。
仅支持 API 28+
还可能需要硬件编码支持,不能保证所有 API28 以上的设备都可用。
inSampleSize
该参数仅支持 Anrdoid 。
关于该参数的描述,参考 Android 官方网站 。
keepExif
如果该参数为 true,EXIF 信息会保存在压缩后的文件中。
有以下几点需要注意:
- 默认值为 false 。
- 即使设为 true ,也不会包含方向属性。
- 仅支持 jpg 格式,不支持 PNG 格式。
结果
About List<int> and Uint8List
运行时错误
例:
Future<Uint8List> compressAndTryCatch(String path) async {
Uint8List result;
try {
result = await FlutterImageCompress.compressWithFile(path,
format: CompressFormat.heic);
} on UnsupportedError catch (e) {
print(e);
result = await FlutterImageCompress.compressWithFile(path,
format: CompressFormat.jpeg);
}
return result;
}
Android
可能需要升级 Kotlin 到 1.3.72 版本或更高。
iOS
暂时未发现问题。
Troubleshooting or common error
因为一些支持问题,所有的 API 需要兼容格式和系统兼容性,可能会抛出一个异常(UnsupportError)。所以如果 坚持使用 webp 和 heic 格式,请自己捕获异常,然后在不支持的设备上使用 jpeg 压缩。
Compressing returns null
有时,压缩处理会返回 null 。需要检查能否读写文件,还有目标文件的父文件夹必须存在。
例如:使用 path_provider 插件访问一些应用文件夹,还有权限插件在 Android/iOS 上请求访问 SD 卡。
Android build error
Caused by: org.gradle.internal.event.ListenerNotificationException: Failed to notify project evaluation listener.
at org.gradle.internal.event.AbstractBroadcastDispatch.dispatch(AbstractBroadcastDispatch.java:86)
...
Caused by: java.lang.AbstractMethodError
at org.jetbrains.kotlin.gradle.plugin.KotlinPluginKt.resolveSubpluginArtifacts(KotlinPlugin.kt:776)
...
参考 flutter/flutter/issues#21473
需要升级 Kotlin 版本到 1.2.71+ (推荐1.3.72)。
如果将来 Flutter 支持更多的平台(Windows、 Mac、 Linux、等),而你还在使用该库,请提议 issue 或 PR!
About EXIF information
使用该库, EXIF 信息默认会被移除。
将 keepExif 设为 true 可保持 EXIF 信息,但是不会保留 direction 信息。
LICENSE
代码使用 MIT 风格的许可证
PNG/JPEG encoder
分别使用系统的 API 。
Webp encoder
在 iOS 上使用 SDWebImageWebPCoder 来编码 UIImage。 (MIT许可)
Android 代码使用 Android 的系统 api 。
HEIF encoder
在 iOS 上使用 iOS 系统的 api 。
在 AndroidP 或更高版本使用 HeifWriter(androidx component by Google) 编码。
About Exif handle code
iOS 代码从 dvkch/SYPictureMetadata 复制,许可证
Android 代码从 flutter/plugin/image_picker 复制,做了一些改动。(BSD 3 风格许可证)