UNPKG

watch-xdelta

Version:

smart-watch xdelta script

168 lines (142 loc) 5.03 kB
/** * 生成差分文件 */ 'use strict'; import child_process from 'child_process'; import fs from 'fs'; import babel from 'babel'; import bfs from 'babel-fs'; import co from 'co'; import ss from 'stream-stream'; import {generatorHeader, DIFF_INDEX} from './header'; import {generatorFooter} from './footer'; import {truncateLast32Bits} from './util'; const {min, ceil} = Math; const K = 1024; const FOOTER_LENGTH = 32; /** * 切分文件的 * @param {String} oldPath 旧版本的路径 * @param {String} truncatedNewPath 新版本的路径 * @param {String} diffPath 差分文件的路径 * @param [Number] size, default=64k 分块的大小 * @param [Number] softwareVersion, default=0, 软件的版本, 8位16进制, 比如12345678 * @param [Number] hardtwareVersion, default=0, 硬件的版本, 8位16进制 * @return {Promise} 差分文件的总大小 * */ const xdelta = co.wrap(function *({oldPath, newPath, diffPath, size=64, softwareVersion=0, hardwareVersion=0}) { const BLKSIZE = parseInt(size)*K; let oldStat, newStat, oldFd, newFd, truncatedOldPath, truncatedNewPath, v2Tail; try{ let oldFile = yield truncateLast32Bits(oldPath); let newFile = yield truncateLast32Bits(newPath); truncatedOldPath = oldFile.tempFile; truncatedNewPath = newFile.tempFile; v2Tail = newFile.tail; oldStat = yield bfs.stat(truncatedOldPath); newStat = yield bfs.stat(truncatedNewPath); oldFd = yield bfs.open(truncatedOldPath, 'r'); newFd = yield bfs.open(truncatedNewPath, 'r'); }catch(e){ console.error(`${truncatedOldPath} or ${truncatedNewPath} 不存在`); } let largerFile = oldStat.size>newStat.size ? truncatedOldPath : truncatedNewPath; let blk = ceil(min(oldStat.size, newStat.size)/BLKSIZE); let removeFiles = ''; let xdeltaStream = ss(); let xdeltaFiles = []; let diffLen = 0; for(let i=0, len=blk; i<len; i++){ let buf, maxSize = BLKSIZE, oldFile = `v1_${i+1}.bin`, newFile = `v2_${i+1}.bin`, dFile = `d${i+1}.bin`; if(i == len-1){ if(largerFile == oldFile){ maxSize = oldStat.size-BLKSIZE*(blk-1); }else{ maxSize = newStat.size-BLKSIZE*(blk-1); } } buf = new Buffer(maxSize); try { yield sliceFile({fd: oldFd, buffer: buf, filename: oldFile}); yield sliceFile({fd: newFd, buffer: buf, filename: newFile}); yield exec(`xdelta3 -9 -A -f -e -s ${oldFile} ${newFile} ${dFile}`); }catch(e) { console.error(`生成${dFile}出错`); } diffLen += 4; removeFiles += `${oldFile} ${newFile} ${dFile} `; xdeltaFiles.push(dFile); } let header = yield generatorHeader({blk: blk, blksize: BLKSIZE, oldFromLenB: oldStat.size, newFromLenB: newStat.size, xdeltaFiles: xdeltaFiles}); // xdeltaStream是包含了所有差分文件内容的Transform Stream xdeltaFiles.forEach((file) => { xdeltaStream.write(fs.createReadStream(file)); }); xdeltaStream.end(); let ws = fs.createWriteStream(diffPath); let totalLen = yield streamToPromise(xdeltaStream, ws, header, v2Tail); removeFiles += `${truncatedOldPath} ${truncatedNewPath}`; yield exec(`rm -rf ${removeFiles}`); //删除中间文件 return totalLen; }); /** * 按指定缓存大小切分文件 * @param {Number} fd 待切分的文件句柄 * @param {Buffer} buffer 读写文件时用的缓存 * @param {String} filename 生成的切分文件的文件名 * */ const sliceFile = co.wrap(function *({fd, buffer, filename}) { let filesize = buffer.length; let nBytes = yield bfs.read(fd, buffer, 0, filesize, null); yield bfs.writeFile(filename, buffer.slice(0, nBytes)); }); const exec = function(command){ return new Promise((resolve, reject) => { child_process.exec.call(null, command, (err, stdout, stderr) => { if(err){ reject(stderr); }else{ resolve(stdout); } }); }); }; /** * 流操作转化成promise形式,将差分文件块的内容最终合成到一个文件中 * @param {ReadStream} readStream xdeltaStream * @param {WriteStream} writeStream 生成的差分文件的可写流 * @param {Number} header 文件头 * @param {Buffer} tail 尾部32字节 * @return {Promise.<Number>} 返回文件的总大小 * */ const streamToPromise = function(readStream, writeStream, header, tail) { let headerLen = header.length; let totalLen = headerLen; let totalBufArr = [header]; return new Promise(function(resolve) { readStream.on('data', (chunk) => { totalLen += chunk.length; totalBufArr.push(chunk); }); readStream.on('end', function() { let totalBuf = Buffer.concat(totalBufArr); let checksum = 0; //校验和 for(let i=0, len=totalBuf.length; i<len; i++) { checksum += totalBuf[i]; } writeStream.write(totalBuf); let footerBuf = generatorFooter(tail, checksum); writeStream.write(footerBuf); totalLen += FOOTER_LENGTH; resolve(totalLen); }); }); }; export default xdelta;