diff --git a/lib/downloader.js b/lib/downloader.js index 0c56cc3..a093e2e 100644 --- a/lib/downloader.js +++ b/lib/downloader.js @@ -3,6 +3,7 @@ * 详细设计文档可查看 https://juejin.im/post/5b42d3ede51d4519277b6ce3 */ const util = require('./util'); +const sha1 = require('./sha1'); const SAVED_FILES_KEY = 'savedFiles'; const KEY_TOTAL_SIZE = 'totalSize'; @@ -40,53 +41,139 @@ export default class Dowloader { resolve(url); return; } + const fileName = getFileName(url); if (!lru) { // 无 lru 情况下直接判断 临时文件是否存在,不存在重新下载 wx.getFileInfo({ - filePath: url, + filePath: fileName, success: () => { resolve(url); }, fail: () => { - downloadFile(url, lru).then((path) => { - resolve(path); - }, () => { - reject(); - }); + if (util.isOnlineUrl(url)) { + downloadFile(url, lru).then((path) => { + resolve(path); + }, () => { + reject(); + }); + } else if (util.isDataUrl(url)) { + transformBase64File(url, lru).then(path => { + resolve(path); + }, () => { + reject(); + }); + } }, }) return } - const file = getFile(url); + const file = getFile(fileName); if (file) { - // 检查文件是否正常,不正常需要重新下载 - wx.getSavedFileInfo({ - filePath: file[KEY_PATH], - success: (res) => { - resolve(file[KEY_PATH]); - }, - fail: (error) => { - console.error(`the file is broken, redownload it, ${JSON.stringify(error)}`); - downloadFile(url, lru).then((path) => { - resolve(path); - }, () => { - reject(); - }); - }, - }); + if (file[KEY_PATH].indexOf('//usr/') !== -1) { + wx.getFileInfo({ + filePath: file[KEY_PATH], + success() { + resolve(file[KEY_PATH]); + }, + fail(error) { + console.error(`base64 file broken, ${JSON.stringify(error)}`); + transformBase64File(url, lru).then(path => { + resolve(path); + }, () => { + reject(); + }); + } + }) + } else { + // 检查文件是否正常,不正常需要重新下载 + wx.getSavedFileInfo({ + filePath: file[KEY_PATH], + success: (res) => { + resolve(file[KEY_PATH]); + }, + fail: (error) => { + console.error(`the file is broken, redownload it, ${JSON.stringify(error)}`); + downloadFile(url, lru).then((path) => { + resolve(path); + }, () => { + reject(); + }); + }, + }); + } } else { - downloadFile(url, lru).then((path) => { - resolve(path); - }, () => { - reject(); - }); + if (util.isOnlineUrl(url)) { + downloadFile(url, lru).then((path) => { + resolve(path); + }, () => { + reject(); + }); + } else if (util.isDataUrl(url)) { + transformBase64File(url, lru).then(path => { + resolve(path); + }, () => { + reject(); + }); + } } }); } } +function getFileName(url) { + if (util.isDataUrl(url)) { + const [, format, bodyData] = /data:image\/(\w+);base64,(.*)/.exec(url) || []; + const fileName = `${sha1.hex_sha1(bodyData)}.${format}`; + return fileName; + } else { + return url; + } +} + +function transformBase64File(base64data, lru) { + return new Promise((resolve, reject) => { + const [, format, bodyData] = /data:image\/(\w+);base64,(.*)/.exec(base64data) || []; + if (!format) { + console.error('base parse failed'); + reject(); + return; + } + const fileName = `${sha1.hex_sha1(bodyData)}.${format}`; + const path = `${wx.env.USER_DATA_PATH}/${fileName}`; + const buffer = wx.base64ToArrayBuffer(bodyData.replace(/[\r\n]/g, "")); + wx.getFileSystemManager().writeFile({ + filePath: path, + data: buffer, + encoding: 'binary', + success() { + wx.getFileInfo({ + filePath: path, + success: (tmpRes) => { + const newFileSize = tmpRes.size; + lru ? doLru(newFileSize).then(() => { + saveFile(fileName, newFileSize, path, true).then((filePath) => { + resolve(filePath); + }); + }, () => { + resolve(path); + }) : resolve(path); + }, + fail: (error) => { + // 文件大小信息获取失败,则此文件也不要进行存储 + console.error(`getFileInfo ${path} failed, ${JSON.stringify(error)}`); + resolve(path); + }, + }); + }, + fail(err) { + console.log(err) + } + }) + }); +} + function downloadFile(url, lru) { return new Promise((resolve, reject) => { wx.downloadFile({ @@ -127,8 +214,22 @@ function downloadFile(url, lru) { }); } -function saveFile(key, newFileSize, tempFilePath) { +function saveFile(key, newFileSize, tempFilePath, isDataUrl = false) { return new Promise((resolve, reject) => { + if (isDataUrl) { + const totalSize = savedFiles[KEY_TOTAL_SIZE] ? savedFiles[KEY_TOTAL_SIZE] : 0; + savedFiles[key] = {}; + savedFiles[key][KEY_PATH] = tempFilePath; + savedFiles[key][KEY_TIME] = new Date().getTime(); + savedFiles[key][KEY_SIZE] = newFileSize; + savedFiles['totalSize'] = newFileSize + totalSize; + wx.setStorage({ + key: SAVED_FILES_KEY, + data: savedFiles, + }); + resolve(tempFilePath); + return; + } wx.saveFile({ tempFilePath: tempFilePath, success: (fileRes) => { @@ -229,12 +330,21 @@ function removeFiles(pathsShouldDelete) { if (typeof pathDel === 'object') { delPath = pathDel.filePath; } - wx.removeSavedFile({ - filePath: delPath, - fail: (error) => { - console.error(`removeSavedFile ${pathDel} failed, ${JSON.stringify(error)}`); - }, - }); + if (delPath.indexOf('//usr/') !== -1) { + wx.getFileSystemManager().unlink({ + filePath: delPath, + fail(error) { + console.error(`removeSavedFile ${pathDel} failed, ${JSON.stringify(error)}`); + } + }) + } else { + wx.removeSavedFile({ + filePath: delPath, + fail: (error) => { + console.error(`removeSavedFile ${pathDel} failed, ${JSON.stringify(error)}`); + }, + }); + } } } diff --git a/lib/sha1.js b/lib/sha1.js new file mode 100644 index 0000000..e743ec1 --- /dev/null +++ b/lib/sha1.js @@ -0,0 +1,97 @@ +var hexcase = 0; +var chrsz = 8; + +function hex_sha1(s) { + return binb2hex(core_sha1(str2binb(s), s.length * chrsz)); +} + +function core_sha1(x, len) { + x[len >> 5] |= 0x80 << (24 - (len % 32)); + x[(((len + 64) >> 9) << 4) + 15] = len; + + var w = Array(80); + var a = 1732584193; + var b = -271733879; + var c = -1732584194; + var d = 271733878; + var e = -1009589776; + + for (var i = 0; i < x.length; i += 16) { + var olda = a; + var oldb = b; + var oldc = c; + var oldd = d; + var olde = e; + + for (var j = 0; j < 80; j++) { + if (j < 16) w[j] = x[i + j]; + else w[j] = rol(w[j - 3] ^ w[j - 8] ^ w[j - 14] ^ w[j - 16], 1); + var t = safe_add( + safe_add(rol(a, 5), sha1_ft(j, b, c, d)), + safe_add(safe_add(e, w[j]), sha1_kt(j)) + ); + e = d; + d = c; + c = rol(b, 30); + b = a; + a = t; + } + + a = safe_add(a, olda); + b = safe_add(b, oldb); + c = safe_add(c, oldc); + d = safe_add(d, oldd); + e = safe_add(e, olde); + } + return Array(a, b, c, d, e); +} + +function sha1_ft(t, b, c, d) { + if (t < 20) return (b & c) | (~b & d); + if (t < 40) return b ^ c ^ d; + if (t < 60) return (b & c) | (b & d) | (c & d); + return b ^ c ^ d; +} + +function sha1_kt(t) { + return t < 20 + ? 1518500249 + : t < 40 + ? 1859775393 + : t < 60 + ? -1894007588 + : -899497514; +} + +function safe_add(x, y) { + var lsw = (x & 0xffff) + (y & 0xffff); + var msw = (x >> 16) + (y >> 16) + (lsw >> 16); + return (msw << 16) | (lsw & 0xffff); +} + +function rol(num, cnt) { + return (num << cnt) | (num >>> (32 - cnt)); +} + +function str2binb(str) { + var bin = Array(); + var mask = (1 << chrsz) - 1; + for (var i = 0; i < str.length * chrsz; i += chrsz) + bin[i >> 5] |= (str.charCodeAt(i / chrsz) & mask) << (24 - (i % 32)); + return bin; +} + +function binb2hex(binarray) { + var hex_tab = hexcase ? "0123456789ABCDEF" : "0123456789abcdef"; + var str = ""; + for (var i = 0; i < binarray.length * 4; i++) { + str += + hex_tab.charAt((binarray[i >> 2] >> ((3 - (i % 4)) * 8 + 4)) & 0xf) + + hex_tab.charAt((binarray[i >> 2] >> ((3 - (i % 4)) * 8)) & 0xf); + } + return str; +} + +module.exports = { + hex_sha1, +} \ No newline at end of file diff --git a/lib/util.js b/lib/util.js index 292df2a..a18808d 100644 --- a/lib/util.js +++ b/lib/util.js @@ -1,6 +1,14 @@ function isValidUrl(url) { - return /(ht|f)tp(s?):\/\/([^ \\/]*\.)+[^ \\/]*(:[0-9]+)?\/?/.test(url); + return isOnlineUrl(url) || isDataUrl(url); +} + +function isOnlineUrl(url) { + return /(ht|f)tp(s?):\/\/([^ \\/]*\.)+[^ \\/]*(:[0-9]+)?\/?/.test(url) +} + +function isDataUrl(url) { + return /data:image\/(\w+);base64,(.*)/.test(url); } /** @@ -63,6 +71,8 @@ function equal(a, b) { module.exports = { isValidUrl, + isOnlineUrl, + isDataUrl, equal };