在静态资源上传和管理上,尤其是分片上传时,我们经常会用到文件hash。今天给大家介绍几种文件hash的实现方法。下图是几种hash的耗时和hash值。
文件hash的概念
通过hash算法将文件内容转为固定长度的字符串,用于文件内容的唯一标识。
文件hash的应用场景
- 验证数据的完整性
- 文件内容的唯一标识
- 重复文件检测
- 版本控制
常用的hash算法
MD5、SHA-1、SHA-256、SHA-384、SHA-512
hash算法比较
- MD5,最快,但存在碰撞风险,属于非加密hash
- SHA-1 较快,但存在碰撞风险, 属于加密hash
- SHA-256 中等,安全性较高,属于加密hash
- SHA-384 中等,安全性高,属于加密hash
- SHA-512 中等,安全性非常高,属于加密hash
代码实现
- 原生js提供的加密hash方法,对于1000M以下的文件,速度非常快,超大文件时(大于1G时)速度中等
async function calculateNativeHash(file: File) {
const buffer = await file.arrayBuffer();
const hashBuffer = await crypto.subtle.digest('SHA-256', buffer);
const hashArray = Array.from(new Uint8Array(hashBuffer));
return hashArray.map(b => b.toString(16).padStart(2, '0')).join('');
}
上图可视:文件大小 734.98 MB 耗时:2.52秒
- 使用crpto-js,速度较慢,尤其大文件会出现非常明显的卡顿
async function calculateFileHashByChunks(file: File) {
return new Promise((resolve, reject) => {
const reader = new FileReader();
const chunkSize = 1024 * 1024 * 50; // 50MB分片
let currentChunk = 0;
const totalChunks = Math.ceil(file.size / chunkSize);
const hash = CryptoJS.algo.MD5.create();
reader.onload = async (e) => {
try {
// 转换分片数据
const buffer = new Uint8Array(e.target?.result as ArrayBuffer);
const wordArray = CryptoJS.lib.WordArray.create(buffer);
hash.update(wordArray);
currentChunk++;
if (currentChunk < totalchunks loadnextchunk else resolvehash.finalize.tostring catch error rejecterror const loadnextchunk='()'> {
const start = currentChunk * chunkSize;
const end = Math.min(start + chunkSize, file.size);
reader.readAsArrayBuffer(file.slice(start, end));
};
reader.onerror = reject;
loadNextChunk();
});
}
上图可视:文件大小 734.98 MB 耗时:28.02秒
- spark-md5,速度中等,即使使用分块或者worker,速度不会有很多提升
async function calculateSparkHash(file: File) {
const spark = new SparkMD5.ArrayBuffer();
const stream = file.stream();
const reader = stream.getReader();
while (true) {
const { done, value } = await reader.read();
if (done) break;
spark.append(value.buffer);
}
return spark.end();
}
上图可视:文件大小 734.98 MB 耗时:8.83秒
- wasm-hash,使用webAssamb1y,速度快,尤其在超大文件时速度比原生js要快的多
const md5 = await createMD5();
md5.init();
const reader = file.stream().getReader();
let n=0
while (true) {
const { done, value } = await reader.read();
console.log('n次数',n++,)
if (done) break;
md5.update(value);
}
return md5.digest('hex');
上图可视:文件大小 734.98 MB 耗时:3.44秒
- 截图中还有其他几种hash算法,是结合worker的,但提升并不是很大,就不列举出来了。需要查看源码的朋友,可以next-demo: next-demo。