flutter 使用archive压缩与解压文件时出现中文乱码的问题

archive 是 flutter 上的一个文件压缩与解压的类库,支持 zip,tar,zlip,gzip,zip2,xz 格式的压缩与解压。

archive 的使用

archive 主要通过 Archive,ArchiveFile,ZipEncoder,ZipDecoder 三个类来实现文件的压缩和解压。

ArchiveFile 表示压缩包内的一个文件。

Archive 表示一个压缩包。

ZipEncoder 表示 zip 编码器。

ZipDecoder 表示 zip 解码器。

文件压缩:

dart 复制代码
final zipFile = File("text.zip");   
final archive = Archive();  
var str="中文";
final archiveFile=ArchiveFile.string("test.txt", str); //将压缩内容添加到ArchiveFile
archive.addFile(archiveFile);  //将ArchiveFile添加到Archive
final zipData = ZipEncoder().encode(archive);  //使用zip编码
zipFile.writeAsBytesSync(zipData!);  //写入到压缩文件

文件解压:

dart 复制代码
final zipFile = File("text.zip");
var archive=ZipDecoder().decodeBytes(zipFile.readAsBytesSync()); //读取文件内容,使用ZipDecoder解码
ArchiveFile? f=archive.findFile("test.txt");  //从压缩包内寻找文件
var content=f?.content;  //获取内容

乱码分析

对于非中文内容来说,直接使用以上代码来实现压缩和解压是不会导致乱码,但是对于中文来说却会导致乱码。

首先来分析 ArchiveFile.string() 方法做了什么。

dart 复制代码
ArchiveFile.string(this.name, String content,  
    [this._compressionType = STORE]) {  
  size = content.length;  
  _content = Uint8List.fromList(content.codeUnits);  
  _rawContent = InputStream(_content);  
}

能看到,该方法首先获取传入字符串的 utf-16 编码的列表,再将其转为 uint8 列表。uint8 在 flutter 中表示 8 位比特(bit)的无符号数,范围为 [0,256)。因为文件就是按字节(Byte)存储的,一个字节由 8 位比特组成,范围跟 uint8 一致。

但是对于中文来说,大小是超过 256 的,所以当使用 Uint8List.fromList() 将中文从 utf-16 强转为 uint8,会导致其被截断,从而导致乱码。

从下面测试例子中也能看出来:

dart 复制代码
String str="中文";  
var list=str.codeUnits;  
print(list);  
var list2=Uint8List.fromList(list);  
print(list2);

//运行结果为:
[20013, 25991]
[45, 135]

所以对于中文来说,并不能直接强制为 uint8,需要对其进行编码再进行存储,中文编码方式存在 utf-8,gbk 等,而 flutter 支持 utf-8 编码,但不支持 gbk 编码。对于 utf-8 编码来说,中文需要 3 个字节来存储。

dart 复制代码
utf8.encode(); //编码
utf8.decode(); //解码

我们再来看,ArchiveFile 的构造方法除了 ArchiveFile.string() 外,还有

dart 复制代码
ArchiveFile(this.name, this.size, dynamic content,  
    [this._compressionType = STORE]) {  
  name = name.replaceAll('\\', '/');  
  if (content is Uint8List) {  
    _content = content;  
    _rawContent = InputStream(_content);  
    if (size <= 0) {  
      size = content.length;  
    }  
  } else if (content is InputStream) {  
    _rawContent = InputStream.from(content);  
    if (size <= 0) {  
      size = content.length;  
    }  
  } else if (content is InputStreamBase) {  
    _rawContent = content;  
    if (size <= 0) {  
      size = content.length;  
    }  
  } else if (content is TypedData) {  
    _content = Uint8List.view(content.buffer);  
    _rawContent = InputStream(_content);  
    if (size <= 0) {  
      size = (_content as Uint8List).length;  
    }  
  } else if (content is String) {  
    _content = content.codeUnits;  
    _rawContent = InputStream(_content);  
    if (size <= 0) {  
      size = content.codeUnits.length + 1;  
    }  
  } else if (content is List<int>) {  
    // Legacy  
    // This expects the list to be a list of bytes, with values [0, 255].  
    _content = content;  
    _rawContent = InputStream(_content);  
    if (size <= 0) {  
      size = content.length;  
    }  
  } else if (content is FileContent) {  
    _content = content;  
  }  
}

当传入的 content 类型为 List<int> 时,直接赋值为 _content,没有进行额外的操作。另外该方法还有另外一个参数 _compressionType,该参数需要传入 ArchiveFile.STORE,否则在进行解压时,archiveFile.content 会报错:Unhandled Exception: RangeError: Value not in range: -6128

解决办法

所以最后处理中文乱码的解决办法就是先对其进行 utf-8 编码,再压缩。解压后,对其进行 utf-8 解码,获取中文内容。

代码如下:

dart 复制代码
//压缩
final zipFile = File("text.zip");  
final archive = Archive();  
var str="中文";
//这步是必须的
var encodeStr=utf8.encode(str);   
//必须要为ArchiveFile.STORE
final archiveFile=ArchiveFile("test.txt",str.length,encodeStr,ArchiveFile.STORE); 
archive.addFile(archiveFile);  
final zipData =  ZipEncoder().encode(archive);  
zipFile.writeAsBytesSync(zipData!);  

//解压
ArchiveFile? 
f=ZipDecoder().decodeBytes(zipFile.readAsBytesSync()).findFile("test.txt");  
var content=f?.content;  
// 解码
print(utf8.decode(content));
相关推荐
传奇开心果11 分钟前
【Flutter踩坑排雷】第5课:扩展方法冲突坑|同名扩展引发编译歧义,故障规避手段
android·flutter
雪铃儿21 小时前
Flutter 双端热更新新方案:基于 Dart 官方动态模块的 flutter_patcher 1.0.0
flutter·hotfix
好的佩奇1 天前
Dart 之方法、操作符与可调用对象
android·flutter·dart
恋猫de小郭1 天前
Flutter Impeller Vulkan 内存持续上涨,问题没想到是文字阴影
android·前端·flutter
传奇开心果2 天前
【Flutter踩坑排雷】第3课:const 误用坑|const 错误使用引发 UI 异常与性能损耗
android·flutter
行者-全栈开发2 天前
Flutter 插件 device_calendar_plus 开源鸿蒙适配实战:从权限弹窗失灵到循环日程跑通
flutter·跨平台·harmonyos·arkts·日历·适配·开源鸿蒙
恋猫de小郭2 天前
Flutter 开始支持 Jetpack Compose Platform View
android·前端·flutter
SoaringHeart3 天前
Flutter 查缺补漏:@JsonKey 属性全解,让模型层不再被后端字段牵着走
flutter
恋猫de小郭3 天前
Flutter 可以更直接地调用原生 API 了,Dart Direct Native Interop
android·前端·flutter