UNPKG

watch-xdelta

Version:

smart-watch xdelta script

299 lines (236 loc) 9.01 kB
/** * 生成差分文件 */ 'use strict'; var _regeneratorRuntime = require('babel-runtime/regenerator')['default']; var _Promise = require('babel-runtime/core-js/promise')['default']; var _interopRequireDefault = require('babel-runtime/helpers/interop-require-default')['default']; Object.defineProperty(exports, '__esModule', { value: true }); var _child_process = require('child_process'); var _child_process2 = _interopRequireDefault(_child_process); var _fs = require('fs'); var _fs2 = _interopRequireDefault(_fs); var _babel = require('babel'); var _babel2 = _interopRequireDefault(_babel); var _babelFs = require('babel-fs'); var _babelFs2 = _interopRequireDefault(_babelFs); var _co = require('co'); var _co2 = _interopRequireDefault(_co); var _streamStream = require('stream-stream'); var _streamStream2 = _interopRequireDefault(_streamStream); var _header = require('./header'); var _footer = require('./footer'); var _util = require('./util'); var min = Math.min; var ceil = Math.ceil; var K = 1024; var 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} 差分文件的总大小 * */ var xdelta = _co2['default'].wrap(_regeneratorRuntime.mark(function callee$0$0(_ref) { var oldPath = _ref.oldPath; var newPath = _ref.newPath; var diffPath = _ref.diffPath; var _ref$size = _ref.size; var size = _ref$size === undefined ? 64 : _ref$size; var _ref$softwareVersion = _ref.softwareVersion; var softwareVersion = _ref$softwareVersion === undefined ? 0 : _ref$softwareVersion; var _ref$hardwareVersion = _ref.hardwareVersion; var hardwareVersion = _ref$hardwareVersion === undefined ? 0 : _ref$hardwareVersion; var BLKSIZE, oldStat, newStat, oldFd, newFd, truncatedOldPath, truncatedNewPath, v2Tail, oldFile, newFile, largerFile, blk, removeFiles, xdeltaStream, xdeltaFiles, diffLen, i, len, buf, maxSize, dFile, header, ws, totalLen; return _regeneratorRuntime.wrap(function callee$0$0$(context$1$0) { while (1) switch (context$1$0.prev = context$1$0.next) { case 0: BLKSIZE = parseInt(size) * K; oldStat = undefined, newStat = undefined, oldFd = undefined, newFd = undefined, truncatedOldPath = undefined, truncatedNewPath = undefined, v2Tail = undefined; context$1$0.prev = 2; context$1$0.next = 5; return (0, _util.truncateLast32Bits)(oldPath); case 5: oldFile = context$1$0.sent; context$1$0.next = 8; return (0, _util.truncateLast32Bits)(newPath); case 8: newFile = context$1$0.sent; truncatedOldPath = oldFile.tempFile; truncatedNewPath = newFile.tempFile; v2Tail = newFile.tail; context$1$0.next = 14; return _babelFs2['default'].stat(truncatedOldPath); case 14: oldStat = context$1$0.sent; context$1$0.next = 17; return _babelFs2['default'].stat(truncatedNewPath); case 17: newStat = context$1$0.sent; context$1$0.next = 20; return _babelFs2['default'].open(truncatedOldPath, 'r'); case 20: oldFd = context$1$0.sent; context$1$0.next = 23; return _babelFs2['default'].open(truncatedNewPath, 'r'); case 23: newFd = context$1$0.sent; context$1$0.next = 29; break; case 26: context$1$0.prev = 26; context$1$0.t0 = context$1$0['catch'](2); console.error(truncatedOldPath + ' or ' + truncatedNewPath + ' 不存在'); case 29: largerFile = oldStat.size > newStat.size ? truncatedOldPath : truncatedNewPath; blk = ceil(min(oldStat.size, newStat.size) / BLKSIZE); removeFiles = ''; xdeltaStream = (0, _streamStream2['default'])(); xdeltaFiles = []; diffLen = 0; i = 0, len = blk; case 36: if (!(i < len)) { context$1$0.next = 58; break; } buf = undefined, 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); context$1$0.prev = 40; context$1$0.next = 43; return sliceFile({ fd: oldFd, buffer: buf, filename: oldFile }); case 43: context$1$0.next = 45; return sliceFile({ fd: newFd, buffer: buf, filename: newFile }); case 45: context$1$0.next = 47; return exec('xdelta3 -9 -A -f -e -s ' + oldFile + ' ' + newFile + ' ' + dFile); case 47: context$1$0.next = 52; break; case 49: context$1$0.prev = 49; context$1$0.t1 = context$1$0['catch'](40); console.error('生成' + dFile + '出错'); case 52: diffLen += 4; removeFiles += oldFile + ' ' + newFile + ' ' + dFile + ' '; xdeltaFiles.push(dFile); case 55: i++; context$1$0.next = 36; break; case 58: context$1$0.next = 60; return (0, _header.generatorHeader)({ blk: blk, blksize: BLKSIZE, oldFromLenB: oldStat.size, newFromLenB: newStat.size, xdeltaFiles: xdeltaFiles }); case 60: header = context$1$0.sent; // xdeltaStream是包含了所有差分文件内容的Transform Stream xdeltaFiles.forEach(function (file) { xdeltaStream.write(_fs2['default'].createReadStream(file)); }); xdeltaStream.end(); ws = _fs2['default'].createWriteStream(diffPath); context$1$0.next = 66; return streamToPromise(xdeltaStream, ws, header, v2Tail); case 66: totalLen = context$1$0.sent; removeFiles += truncatedOldPath + ' ' + truncatedNewPath; context$1$0.next = 70; return exec('rm -rf ' + removeFiles); case 70: return context$1$0.abrupt('return', totalLen); case 71: case 'end': return context$1$0.stop(); } }, callee$0$0, this, [[2, 26], [40, 49]]); })); /** * 按指定缓存大小切分文件 * @param {Number} fd 待切分的文件句柄 * @param {Buffer} buffer 读写文件时用的缓存 * @param {String} filename 生成的切分文件的文件名 * */ var sliceFile = _co2['default'].wrap(_regeneratorRuntime.mark(function callee$0$0(_ref2) { var fd = _ref2.fd; var buffer = _ref2.buffer; var filename = _ref2.filename; var filesize, nBytes; return _regeneratorRuntime.wrap(function callee$0$0$(context$1$0) { while (1) switch (context$1$0.prev = context$1$0.next) { case 0: filesize = buffer.length; context$1$0.next = 3; return _babelFs2['default'].read(fd, buffer, 0, filesize, null); case 3: nBytes = context$1$0.sent; context$1$0.next = 6; return _babelFs2['default'].writeFile(filename, buffer.slice(0, nBytes)); case 6: case 'end': return context$1$0.stop(); } }, callee$0$0, this); })); var exec = function exec(command) { return new _Promise(function (resolve, reject) { _child_process2['default'].exec.call(null, command, function (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>} 返回文件的总大小 * */ var streamToPromise = function streamToPromise(readStream, writeStream, header, tail) { var headerLen = header.length; var totalLen = headerLen; var totalBufArr = [header]; return new _Promise(function (resolve) { readStream.on('data', function (chunk) { totalLen += chunk.length; totalBufArr.push(chunk); }); readStream.on('end', function () { var totalBuf = Buffer.concat(totalBufArr); var checksum = 0; //校验和 for (var i = 0, len = totalBuf.length; i < len; i++) { checksum += totalBuf[i]; } writeStream.write(totalBuf); var footerBuf = (0, _footer.generatorFooter)(tail, checksum); writeStream.write(footerBuf); totalLen += FOOTER_LENGTH; resolve(totalLen); }); }); }; exports['default'] = xdelta; module.exports = exports['default']; //删除中间文件