// 计算文件的eTag,参数为buffer或者readableStream或者文件路径 function getEtag(buffer, callback) { // 判断传入的参数是buffer还是stream还是filepath var mode = 'buffer'; if (typeof buffer === 'string') { buffer = require('fs').createReadStream(buffer); mode = 'stream'; } else if (buffer instanceof require('stream')) { mode = 'stream'; } // sha1算法 var sha1 = function (content) { var crypto = require('crypto'); var sha1 = crypto.createHash('sha1'); sha1.update(content); return sha1.digest(); }; // 以4M为单位分割 var blockSize = 4 * 1024 * 1024; var sha1String = []; var prefix = 0x16; var blockCount = 0; switch (mode) { case 'buffer': var bufferSize = buffer.length; blockCount = Math.ceil(bufferSize / blockSize); for (var i = 0; i < blockCount; i++) { sha1String.push(sha1(buffer.slice(i * blockSize, (i + 1) * blockSize))); } process.nextTick(function () { callback(null, calcEtag()); }); break; case 'stream': var stream = buffer; stream.on('readable', function () { var chunk; while (chunk = stream.read(blockSize)) { sha1String.push(sha1(chunk)); blockCount++; } }); stream.on('end', function () { callback(null, calcEtag()); }); break; } function calcEtag() { if (!sha1String.length) { return 'Fto5o-5ea0sNMlW_75VgGJCv2AcJ'; } var sha1Buffer = Buffer.concat(sha1String, blockCount * 20); // 如果大于4M,则对各个块的sha1结果再次sha1 if (blockCount > 1) { prefix = 0x96; sha1Buffer = sha1(sha1Buffer); } sha1Buffer = Buffer.concat( [new Buffer([prefix]), sha1Buffer], sha1Buffer.length + 1 ); return sha1Buffer.toString('base64') .replace(/\//g, '_').replace(/\+/g, '-'); } } module.exports = getEtag;