Cocos Creator 熱更新文件MD5計算和需要註意的問題
阿新 • • 發佈:2018-08-07
req ole build 完成後 return ted xe8 ati fse
cc.audioEngine.stopAll();
cc.game.restart();
//2.在main.js(build-templates/jsb-default/main.js)裏,cc.game.run(option, onStart);之前 讀取我們記錄的HotUpdateSearchPaths,然後設置sarchPaths
Creator的熱更新使用jsb。熱更新基本按照 http://docs.cocos.com/creator/manual/zh/advanced-topics/hot-update.html?h=%E7%83%AD%E6%9B%B4%E6%96%B0這個官方教程,
以及https://github.com/cocos-creator/tutorial-hot-update這個官方示例就行。但是,有一些地方沒有提及,這會導致熱更出現的問題。
1.自己保存熱更目錄到localstorage,進入時先讀取這個目錄,然後設置成資源查找目錄。
//1.在資源更新完畢後,重啟的時候加入以下處理 var searchPaths = jsb.fileUtils.getSearchPaths(); var newPaths = this._am.getLocalManifest().getSearchPaths(); for(let i = 0;i<newPaths.length;i++){ if(searchPaths.indexOf(newPaths[i]) == -1){ Array.prototype.unshift(searchPaths, newPaths[i]); } } jsb.fileUtils.setSearchPaths(searchPaths); cc.sys.localStorage.setItem(‘HotUpdateSearchPaths‘, JSON.stringify(searchPaths));
//重啟
var searchPaths = jsb.fileUtils.getSearchPaths(); var storePath = cc.sys.localStorage.getItem(‘HotUpdateSearchPaths‘); storePath = JSON.parse(storePath); storePath && jsb.fileUtils.setSearchPaths(storePath);
2.在熱更模塊中
this._am.setVerifyCallback(this._verifyFileHandle.bind(this));可以設置1個文件下載完成後,驗證的函數。返回true就標識通過驗證,該文件才標識更新成功。 但是文檔和示例沒有說明如何在js裏計算文件md5。除此之外官方提供的用於遍歷文件,計算文件md5然後,產生project.manifest、version.manifest 的nodejs腳本version_generator.js在計算文件md5時有一處問題。md5 = crypto.createHash(‘md5‘).update(fs.readFileSync(subpath,"binary")).digest(‘hex‘);
這裏fs.readFileSycn(subpath,"binary")返回的並非二進制類型,而是String。這會導致非文本文件md5計算錯誤。如果寫入到manifest的文件md5是錯誤的,你下載文件後,
計算md5作比較當然就很難匹配上了。除此之外,我們在jsb裏只能使用:
1.jsb.fileUtils.getStringFromFile(filePath); 2.jsb.fileUtils.getDataFromFile(filePath);
獲取文件內容。getStringFromFile無法獲取非文本文件內容。所以我們只能使用getDataFromFile獲取文件二進制數據,它在js層面的視圖類型是Uint8Array和nodejs readFileSync(subpath)一致。
3.前端js計算文件md5
1).改造version_generator.js
md5 = crypto.createHash(‘md5‘).update(fs.readFileSync(subpath,"binary")).digest(‘hex‘);
為
md5 = crypto.createHash(‘md5‘).update(fs.readFileSync(subpath)).digest(‘hex‘);
2)改造引擎自帶的jsb_runtime_md5.js 它用js實現了byteArray md5的計算(大家可以在構建後搜索一下這文件,然後拷貝放到自己的js層工程裏來)。但是這個還不能用,我們要稍微改造它。改造後的文件。
/** * from jsb_runtime_md5.js * @param {} data */ module.exports = function(data){ // for test/debug function fflog(msg) { try { console.log(msg); } catch(e) {} } // convert number to (unsigned) 32 bit hex, zero filled string function to_zerofilled_hex(n) { var t1 = (n >>> 24).toString(16); var t2 = (n & 0x00FFFFFF).toString(16); return "00".substr(0, 2 - t1.length) + t1 + "000000".substr(0, 6 - t2.length) + t2; } // convert a 64 bit unsigned number to array of bytes. Little endian function int64_to_bytes(num) { var retval = []; for (var i = 0; i < 8; i++) { retval.push(num & 0xFF); num = num >>> 8; } return retval; } // 32 bit left-rotation function rol(num, places) { return ((num << places) & 0xFFFFFFFF) | (num >>> (32 - places)); } // The 4 MD5 functions function fF(b, c, d) { return (b & c) | (~b & d); } function fG(b, c, d) { return (d & b) | (~d & c); } function fH(b, c, d) { return b ^ c ^ d; } function fI(b, c, d) { return c ^ (b | ~d); } // pick 4 bytes at specified offset. Little-endian is assumed function bytes_to_int32(arr, off) { return (arr[off + 3] << 24) | (arr[off + 2] << 16) | (arr[off + 1] << 8) | (arr[off]); } // convert the 4 32-bit buffers to a 128 bit hex string. (Little-endian is assumed) function int128le_to_hex(a, b, c, d) { var ra = ""; var t = 0; var ta = 0; for (var i = 3; i >= 0; i--) { ta = arguments[i]; t = (ta & 0xFF); ta = ta >>> 8; t = t << 8; t = t | (ta & 0xFF); ta = ta >>> 8; t = t << 8; t = t | (ta & 0xFF); ta = ta >>> 8; t = t << 8; t = t | ta; ra = ra + to_zerofilled_hex(t); } return ra; } // check input data type and perform conversions if needed if (!data instanceof Uint8Array){ fflog("input data type mismatch only support Uint8Array"); return null; } var databytes = []; for(var i = 0; i < data.byteLength;i++){ databytes.push(data[i]); } // save original length var org_len = databytes.length; // first append the "1" + 7x "0" databytes.push(0x80); // determine required amount of padding var tail = databytes.length % 64; // no room for msg length? if (tail > 56) { // pad to next 512 bit block for (var i = 0; i < (64 - tail); i++) { databytes.push(0x0); } tail = databytes.length % 64; } for (i = 0; i < (56 - tail); i++) { databytes.push(0x0); } // message length in bits mod 512 should now be 448 // append 64 bit, little-endian original msg length (in *bits*!) databytes = databytes.concat(int64_to_bytes(org_len * 8)); // initialize 4x32 bit state var h0 = 0x67452301; var h1 = 0xEFCDAB89; var h2 = 0x98BADCFE; var h3 = 0x10325476; // temp buffers var a = 0, b = 0, c = 0, d = 0; function _add(n1, n2) { return 0x0FFFFFFFF & (n1 + n2) } // function update partial state for each run var updateRun = function(nf, sin32, dw32, b32) { var temp = d; d = c; c = b; //b = b + rol(a + (nf + (sin32 + dw32)), b32); b = _add(b, rol( _add(a, _add(nf, _add(sin32, dw32)) ), b32 ) ); a = temp; }; // Digest message for (i = 0; i < databytes.length / 64; i++) { // initialize run a = h0; b = h1; c = h2; d = h3; var ptr = i * 64; // do 64 runs updateRun(fF(b, c, d), 0xd76aa478, bytes_to_int32(databytes, ptr), 7); updateRun(fF(b, c, d), 0xe8c7b756, bytes_to_int32(databytes, ptr + 4), 12); updateRun(fF(b, c, d), 0x242070db, bytes_to_int32(databytes, ptr + 8), 17); updateRun(fF(b, c, d), 0xc1bdceee, bytes_to_int32(databytes, ptr + 12), 22); updateRun(fF(b, c, d), 0xf57c0faf, bytes_to_int32(databytes, ptr + 16), 7); updateRun(fF(b, c, d), 0x4787c62a, bytes_to_int32(databytes, ptr + 20), 12); updateRun(fF(b, c, d), 0xa8304613, bytes_to_int32(databytes, ptr + 24), 17); updateRun(fF(b, c, d), 0xfd469501, bytes_to_int32(databytes, ptr + 28), 22); updateRun(fF(b, c, d), 0x698098d8, bytes_to_int32(databytes, ptr + 32), 7); updateRun(fF(b, c, d), 0x8b44f7af, bytes_to_int32(databytes, ptr + 36), 12); updateRun(fF(b, c, d), 0xffff5bb1, bytes_to_int32(databytes, ptr + 40), 17); updateRun(fF(b, c, d), 0x895cd7be, bytes_to_int32(databytes, ptr + 44), 22); updateRun(fF(b, c, d), 0x6b901122, bytes_to_int32(databytes, ptr + 48), 7); updateRun(fF(b, c, d), 0xfd987193, bytes_to_int32(databytes, ptr + 52), 12); updateRun(fF(b, c, d), 0xa679438e, bytes_to_int32(databytes, ptr + 56), 17); updateRun(fF(b, c, d), 0x49b40821, bytes_to_int32(databytes, ptr + 60), 22); updateRun(fG(b, c, d), 0xf61e2562, bytes_to_int32(databytes, ptr + 4), 5); updateRun(fG(b, c, d), 0xc040b340, bytes_to_int32(databytes, ptr + 24), 9); updateRun(fG(b, c, d), 0x265e5a51, bytes_to_int32(databytes, ptr + 44), 14); updateRun(fG(b, c, d), 0xe9b6c7aa, bytes_to_int32(databytes, ptr), 20); updateRun(fG(b, c, d), 0xd62f105d, bytes_to_int32(databytes, ptr + 20), 5); updateRun(fG(b, c, d), 0x2441453, bytes_to_int32(databytes, ptr + 40), 9); updateRun(fG(b, c, d), 0xd8a1e681, bytes_to_int32(databytes, ptr + 60), 14); updateRun(fG(b, c, d), 0xe7d3fbc8, bytes_to_int32(databytes, ptr + 16), 20); updateRun(fG(b, c, d), 0x21e1cde6, bytes_to_int32(databytes, ptr + 36), 5); updateRun(fG(b, c, d), 0xc33707d6, bytes_to_int32(databytes, ptr + 56), 9); updateRun(fG(b, c, d), 0xf4d50d87, bytes_to_int32(databytes, ptr + 12), 14); updateRun(fG(b, c, d), 0x455a14ed, bytes_to_int32(databytes, ptr + 32), 20); updateRun(fG(b, c, d), 0xa9e3e905, bytes_to_int32(databytes, ptr + 52), 5); updateRun(fG(b, c, d), 0xfcefa3f8, bytes_to_int32(databytes, ptr + 8), 9); updateRun(fG(b, c, d), 0x676f02d9, bytes_to_int32(databytes, ptr + 28), 14); updateRun(fG(b, c, d), 0x8d2a4c8a, bytes_to_int32(databytes, ptr + 48), 20); updateRun(fH(b, c, d), 0xfffa3942, bytes_to_int32(databytes, ptr + 20), 4); updateRun(fH(b, c, d), 0x8771f681, bytes_to_int32(databytes, ptr + 32), 11); updateRun(fH(b, c, d), 0x6d9d6122, bytes_to_int32(databytes, ptr + 44), 16); updateRun(fH(b, c, d), 0xfde5380c, bytes_to_int32(databytes, ptr + 56), 23); updateRun(fH(b, c, d), 0xa4beea44, bytes_to_int32(databytes, ptr + 4), 4); updateRun(fH(b, c, d), 0x4bdecfa9, bytes_to_int32(databytes, ptr + 16), 11); updateRun(fH(b, c, d), 0xf6bb4b60, bytes_to_int32(databytes, ptr + 28), 16); updateRun(fH(b, c, d), 0xbebfbc70, bytes_to_int32(databytes, ptr + 40), 23); updateRun(fH(b, c, d), 0x289b7ec6, bytes_to_int32(databytes, ptr + 52), 4); updateRun(fH(b, c, d), 0xeaa127fa, bytes_to_int32(databytes, ptr), 11); updateRun(fH(b, c, d), 0xd4ef3085, bytes_to_int32(databytes, ptr + 12), 16); updateRun(fH(b, c, d), 0x4881d05, bytes_to_int32(databytes, ptr + 24), 23); updateRun(fH(b, c, d), 0xd9d4d039, bytes_to_int32(databytes, ptr + 36), 4); updateRun(fH(b, c, d), 0xe6db99e5, bytes_to_int32(databytes, ptr + 48), 11); updateRun(fH(b, c, d), 0x1fa27cf8, bytes_to_int32(databytes, ptr + 60), 16); updateRun(fH(b, c, d), 0xc4ac5665, bytes_to_int32(databytes, ptr + 8), 23); updateRun(fI(b, c, d), 0xf4292244, bytes_to_int32(databytes, ptr), 6); updateRun(fI(b, c, d), 0x432aff97, bytes_to_int32(databytes, ptr + 28), 10); updateRun(fI(b, c, d), 0xab9423a7, bytes_to_int32(databytes, ptr + 56), 15); updateRun(fI(b, c, d), 0xfc93a039, bytes_to_int32(databytes, ptr + 20), 21); updateRun(fI(b, c, d), 0x655b59c3, bytes_to_int32(databytes, ptr + 48), 6); updateRun(fI(b, c, d), 0x8f0ccc92, bytes_to_int32(databytes, ptr + 12), 10); updateRun(fI(b, c, d), 0xffeff47d, bytes_to_int32(databytes, ptr + 40), 15); updateRun(fI(b, c, d), 0x85845dd1, bytes_to_int32(databytes, ptr + 4), 21); updateRun(fI(b, c, d), 0x6fa87e4f, bytes_to_int32(databytes, ptr + 32), 6); updateRun(fI(b, c, d), 0xfe2ce6e0, bytes_to_int32(databytes, ptr + 60), 10); updateRun(fI(b, c, d), 0xa3014314, bytes_to_int32(databytes, ptr + 24), 15); updateRun(fI(b, c, d), 0x4e0811a1, bytes_to_int32(databytes, ptr + 52), 21); updateRun(fI(b, c, d), 0xf7537e82, bytes_to_int32(databytes, ptr + 16), 6); updateRun(fI(b, c, d), 0xbd3af235, bytes_to_int32(databytes, ptr + 44), 10); updateRun(fI(b, c, d), 0x2ad7d2bb, bytes_to_int32(databytes, ptr + 8), 15); updateRun(fI(b, c, d), 0xeb86d391, bytes_to_int32(databytes, ptr + 36), 21); // update buffers h0 = _add(h0, a); h1 = _add(h1, b); h2 = _add(h2, c); h3 = _add(h3, d); } // Done! Convert buffers to 128 bit (LE) return int128le_to_hex(h3, h2, h1, h0).toLowerCase(); };
這裏我把它改造成了只接受Uint8Array數據的格式。它原先只能計算字符串的md5碼。
3)腳本裏的使用方式
//引入我們的md5模塊
const MD5 = require("jsb_runtime_md5");
/** * 指定文件驗證函數 * @param path 下載的文件的本地路徑 * @param asset 下載的資源 */ _verifyFileHandle:function(path, asset){ //服務器上manifest裏對該項資源配置的Md5碼 //asset.md5 //服務器端的相對路徑 //asset.path //是否為壓縮文件 //asset.compressed //文件尺寸 //asset.size //下載狀態 包含 UNSTARTED、DOWNLOADING、SUCCESSED、UNMARKED //asset.downloadState var resMD5 = this.calMD5OfFile(path); return asset.md5 == resMD5; },
calMD5OfFile:function(filePath){return MD5(jsb.fileUtils.getDataFromFile(filePath));}
Cocos Creator 熱更新文件MD5計算和需要註意的問題