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是極爲類似的,你們可自行編寫。