watch-xdelta
Version:
smart-watch xdelta script
299 lines (236 loc) • 9.01 kB
JavaScript
/**
* 生成差分文件
*/
;
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'];
//删除中间文件