微信小程序 - PC端選擇文件
- 分享代碼片段
- 場景分析
- 解決思路
- 附魔腳本
- chooseMediaZip 選擇附魔后的ZIP文件
- 相關方法
- 測試方法
- 參考資料
分享代碼片段
不想聽廢話的,直接看代碼。
https://developers.weixin.qq.com/s/UL9aojmn7iNU
場景分析
如果你的微信小程序需要選中 ZIP 包,然后解壓并處理其中的文件。
對于移動端可以使用 wx.chooseMessageFile(Object object) 來實現,它支持從客戶端會話選擇文件。
但不幸的是PC端,并不支持此API。官方也沒有給出別的API。(當然論壇里有提到一個曲線解決方案,申請個企業號,用webview
實現選擇文件。網上有討論的,這里就不細聊了。)
但是對于個人號來說,這就尷尬了。一是沒有權限使用webview
,二是純前端的小程序,為了選擇文件還得加個服務器,有點不值當。
所以想到了一個曲線的解決方案。利用:chooseMedia
解決思路
- 雖然PC端不支持 wx.chooseMessageFile 但支持 wx.chooseMedia 這個API是用來選擇媒體文件的,比如:視頻、圖片。
- 通過測試,直接將 zip 的擴展名改為 PNG 是無效的。因為選擇后,它會檢查文件頭,過濾掉不符合的,最終只能拿到真實的PNG文件。
- 既然它是校驗了文件頭,那我們就給ZIP文件前加上PNG的文件名,然后通過
wx.chooseMedia
獲取到文件后,再跳過我們自己的 PNG頭,把剩余的ZIP部分讀出來,保存為一個正常的 ZIP 文件。 - 然后就可以正常解壓或讀取 ZIP 壓縮包了。
附魔腳本
將PNG
頭加到ZIP
文件前面,可以使用下面這個腳本實現。
這是附魔的bat
腳本,通過在zip
前添加25
個字節,是PNG
文件的頭89504E470D0A1A0A0000000D49484452
轉成的base64
。
echo iVBORw0KGgoAAAANSUhEUg==>_________
copy /b _________ + /b %1 %~nx1.png
del /q _________
將zip
拖到腳本上之后,即可生成一個 xxx.zip.png
文件。
chooseMediaZip 選擇附魔后的ZIP文件
const fs = wx.getFileSystemManager();/*** 選擇媒體附魔的ZIP* @param {object} options */
function chooseMediaZip(options = {}){const defaultOptions = { count: 9, // 單次選擇數量上限mediaType: ['image'], // 選擇框支持的文件類型encoding: 'binary', // 編碼類型,默認 'binary'。想寫文本用 'utf8'folder: `${wx.env.USER_DATA_PATH}/jerry`, // 保存二進制文件的路徑suffix: 'zip' // 二進制文件的擴展名};let params = {...defaultOptions, ...options};return wx.chooseMedia(params) // 選擇 *.zip.png 文件.then(res=>{ // 跳過 PNG 讀取出實際二進制數據return res.tempFiles.map(tempFile=> {// 跳過PNG頭25字節,從26開始讀取 zip 的二進制部分const arrayBuffer = readFileSync(tempFile.tempFilePath, '', 26);console.log(arrayBuffer);// 寫 二進制文件 到目錄 pathconst file = {};file.name = `${Date.now()}.${suffix}`;file.path =`${ params.folder }/${ file.name }`;mkdir(params.folder); writeFileSync( file.path, arrayBuffer, encoding ); // 寫 二進制 文件return file; // {name: 'fileName', path: 'http://xxx.xxx'}});}).catch(console.error);
}
相關方法
chooseMedia 需要用到的幾個方法
/*** 判斷文件或目錄是否存在* @param {string} path 文件或目錄路徑*/
function isExist(path){try {fs.accessSync(path);return true;} catch (err) {console.log(`文件或目錄不存在:${err.message}`);return false;}
}/*** 創建目錄路* 如果目錄不存在就創建。* @param {string} dirPath 文件夾路徑* @param {boolean} recursive 如果上級目錄不存在,是否自動創建。默認:是*/
function mkdir(path, recursive = true){if(isExist(path) == false){ // 判斷目錄是否存在fs.mkdirSync(path, recursive); // 如果沒有就創建}
}/*** 寫文件* @param {*} filePath 文件路徑。要寫入的文件路徑 (本地路徑)* @param {*} data 要寫入的文本或二進制數據* @param {*} encoding 指定寫入文件的字符編碼。默認 binary*/
function writeFileSync(filePath, data, encoding='binary') { fs.writeFileSync(filePath, data, encoding);
}
測試方法
test(e){fileUtil.chooseMediaZip({ count: 9, // 單次選擇數量上限mediaType: ['image'], // 選擇框支持的文件類型encoding: 'binary', // 編碼類型,默認 'binary'。想寫文本用 'utf8'folder: `${wx.env.USER_DATA_PATH}/jerry`, // 保存二進制文件的路徑suffix: 'zip' // 二進制文件的擴展名}).then(res => {res.forEach(zip => {fs.readZipEntry({'filePath': zip.path,'entries': 'all',success: res => console.log(res.entries), // zip中的文件列表fail: err => console.log(err)});});});}
參考資料
判斷文件/目錄是否存在(同步版) FileSystemManager.accessSync(string path)
創建目錄(同步版) FileSystemManager.mkdirSync(string dirPath, boolean recursive)
寫文件(同步版) FileSystemManager.writeFileSync(string filePath, string|ArrayBuffer data, string encoding)