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));
相关推荐
火柴就是我6 小时前
flutter 之真手势冲突处理
android·flutter
Speed1237 小时前
`mockito` 的核心“打桩”规则
flutter·dart
法的空间7 小时前
Flutter JsonToDart 支持 JsonSchema
android·flutter·ios
恋猫de小郭7 小时前
Android 将强制应用使用主题图标,你怎么看?
android·前端·flutter
玲珑Felone8 小时前
从flutter源码看其渲染机制
android·flutter
ALLIN1 天前
Flutter 三种方式实现页面切换后保持原页面状态
flutter
Dabei1 天前
Flutter 国际化
flutter
Dabei1 天前
Flutter MQTT 通信文档
flutter
Dabei1 天前
Flutter 中实现 TCP 通信
flutter
孤鸿玉1 天前
ios flutter_echarts 不在当前屏幕 白屏修复
flutter