flutter压缩方式通常使用archive插件,可是根据https://pub.dev/packages/archive的介绍看,仅支持以下方式android
Zip (Archive)Tar (Archive)ios
ZLib [Inflate decompression]c++
GZip [Inflate decompression]shell
BZip2 [decompression]
描述中没有对7z的支持,因此只好本身下载源码编译导入。async
有关p7zip的源码结构,你们感兴趣的能够自行找资料了解,咱们直接看在Android中编译的时候,依赖了哪些文件。打开文件p7zip/CPP/ANDROID/7zr/jni/Android.mk
,咱们能够看到全部的-I相关的include目录和LOCAL_SRC_FILES都是在C和CPP两个目录下的,基于代码库最小化考虑,能够把其余的删掉。ide
android ios lib p7zip C CPP
p7zip源码中入口为main函数,在p7zip/CPP/7zip/UI/Console/MainAr.cpp
中,原型为函数
int MY_CDECL main ( #ifndef _WIN32 int numArgs, char *args[] #endif );
根据ffi的相关类型支持和转换,argv很是很差处理,而且因为C++命名空间的存在,非extern "C"的函数编译后的函数名会不同。避免对源码的入侵,咱们封装一层,在p7zip目录下新建p7zip.cpp
。ui
仅支持.7z的压缩的话,咱们使用7zr便可,咱们使用命令方式,文档在p7zip/DOC/MANUAL/cmdline/index.htm
中能够看到,具体使用以下spa
# 压缩 7zr a 输出文件名.7z 文件或路径列表 # 解压缩 7zr x 须要解压文件 -o解压路径
所以,咱们的p7zip.cpp
增长一个p7zipShell函数传入指令,调用main.net
extern "C" int p7zipShell(char *cmd) { int numArgs; // 最大支持16个参数 char temp[16][512] = {0}; numArgs = parseCmd(cmd, temp); char *args[16] = {0}; for (int i = 0; i < numArgs; ++i) { args[i] = temp[i]; } return main(numArgs, args); }
咱们的字符串指令传入以后,须要解析出参数列表argv,parseCmd就是干这事的
static int parseCmd(char *cmd, char argv[16][512]) { int size = strlen(cmd); int preChar = 0; int a = 0; int b = 0; for (int i = 0; i < size; ++i) { char c = cmd[i]; switch (c) { case ' ': case '\t': if (preChar == 1) { argv[a][b++] = '\0'; a++; b = 0; preChar = 0; } break; default: preChar = 1; argv[a][b++] = c; break; } } if (cmd[size - 1] != ' ' && cmd[size - 1] != '\t') { argv[a][b] = '\0'; a++; } return a; }
最后再导入头文件支持和main函数声明
#include <string.h>; #include "C/7zTypes.h"; extern int MY_CDECL main ( int numArgs, char *args[] );
至此,cpp文件写完。
咱们将cpp文件加入到Android.mk
文件中
LOCAL_SRC_FILES := \ ... ../../../../p7zip.cpp \
再将原先编译成可执行文件改为动态连接库
#include $(BUILD_EXECUTABLE) include $(BUILD_SHARED_LIBRARY)
在打开Application.mk
文件,修改要编译的ABI
APP_ABI := armeabi-v7a arm64-v8a APP_PLATFORM := android-14
native完成,启用ndk编译
# 找到你的sdk下的ndk目录,加入到PATH中 ndk-build
编译完成后,lib生成到p7zip/CPP/ANDROID/7zr/libs
中,暂时先记下。
pubspec.yaml
中增长so资源
flutter: assets: - p7zip/CPP/ANDROID/7zr/libs/arm64-v8a/lib7zr.so - p7zip/CPP/ANDROID/7zr/libs/armeabi-v7a/lib7zr.so
新建p7zip.dart
,因为压缩解压缩是阻塞式,因此咱们要把指令执行任务放在isolate中
// 传入须要压缩的文件列表,以及压缩文件的路径 Future<String> compress(List<String> files, {String path}) async { // 获取共享库路径 final soPath = await _checkSharedLibrary(); if (soPath == null) { return null; } ... // 文件列表转化为字符串 String filesStr = ""; files.forEach((element) { filesStr += " $element"; }); // 执行isolate任务 final receivePort = ReceivePort(); await Isolate.spawn(_shell, [ receivePort.sendPort, soPath, "7zr a $path $filesStr" ]); // 等待任务完成,获得执行结果,0表示执行成功 final result = await receivePort.first; print("[p7zip] compress: after first result = $result"); return result == 0 ? path : null; }
isolate任务,调用p7zipShell函数
// dart <=> native函数原型定义 typedef _NativeP7zipShell = Int32 Function(Pointer<Int8>); typedef _DartP7zipShell = int Function(Pointer<Int8>); void _shell(List argv) { // 传递进来的参数列表转化 final SendPort sendPort = argv[0]; final String soPath = argv[1]; final String cmd = argv[2]; // 打开动态连接库 final p7zip = DynamicLibrary.open(soPath); if (p7zip == null) { return null; } // 获得native中的p7zipShell函数 final _DartP7zipShell p7zipShell = p7zip.lookup<NativeFunction<_NativeP7zipShell>>("p7zipShell") .asFunction(); if (p7zipShell == null) { return null; } // 把dart的String转化为c++中的char * final cstr = _toNativeStr(cmd); final result = p7zipShell.call(cstr); // 通知主线程任务执行结果 sendPort.send(result); }
核心的_checkSharedLibrary把动态连接库从assets中取出来,拷贝到cache目录下。
Future<String> _checkSharedLibrary() async { // 把so放在临时路径中 final dir = await getTemporaryDirectory(); if (dir == null) { return null; } final libFile = File(dir.path + "/lib7zr.so"); final exist = await libFile.exists(); if (exist) { return libFile.path; } // 获取系统 if (Platform.isAndroid) { // 获取abi final devicePlugin = DeviceInfoPlugin(); final deviceInfo = await devicePlugin.androidInfo; if (deviceInfo == null) { return null; } // 这里的soResource就是前面p7zip编译生成的库路径 String soResource = "p7zip/CPP/ANDROID/7zr/libs/armeabi-v7a/lib7zr.so"; final support64 = deviceInfo.supported64BitAbis; if (support64 != null && support64.length > 0) { soResource = "p7zip/CPP/ANDROID/7zr/libs/arm64-v8a/lib7zr.so"; } // 从rootBundle加载出assets资源 final data = await rootBundle.load(soResource); if (data == null) { return null; } // 建立文件 final createFile = await libFile.create(); if (createFile == null) { return null; } // 文件以写方式打开 final writeFile = await createFile.open(mode: FileMode.write); if (writeFile == null) { return null; } // 拷贝数据 await writeFile.writeFrom(Uint8List.view(data.buffer)); return libFile.path; } else { // ios平台的是用dylib ... } }
最后,在其余dart文件中使用
final path = await p7zip.compress(files, path: "/sdcard/Download/test.7z");
至于解压缩的的dart部分和compress是极为类似的,你们可自行编写。