一般来说我们会使用crypto获取hash值,如果代码是这样的
javascript
export const calculateHash = (file) => {
return new Promise((resolve, reject) => {
const reader = new FileReader();
reader.onload = function () {
const arrayBuffer = reader.result;
// 使用CryptoJS计算SHA-256哈希
const wordArray = CryptoJS.lib.WordArray.create(arrayBuffer);
const hash = CryptoJS.SHA256(wordArray).toString(CryptoJS.enc.Hex);
resolve(hash);
};
reader.onerror = reject;
reader.readAsArrayBuffer(file);
});
}
reader.readAsArrayBuffer(file);
会一次性读完整个文件,造成内存暴涨,所以需要修改hash值问题,两个方案
方案1:
❌ 前端不算 hash
✅ 后端生成 key(UUID)
✅ 前端直接上传
方案2:
前端 只算前 5MB + 后 5MB
拼一个"弱 hash"
用于去重,不阻塞 UI
方案2代码:
javascript
export const calculateWeakHash = (file, chunkSize = 5 * 1024 * 1024) => {
return new Promise((resolve, reject) => {
const size = file.size
// 小文件直接走全量
if (size <= chunkSize * 2) {
return calculateFullHash(file).then(resolve).catch(reject)
}
const firstChunk = file.slice(0, chunkSize)
const lastChunk = file.slice(size - chunkSize, size)
const reader1 = new FileReader()
const reader2 = new FileReader()
let buffer1, buffer2
reader1.onload = () => {
buffer1 = reader1.result
if (buffer2) finish()
}
reader2.onload = () => {
buffer2 = reader2.result
if (buffer1) finish()
}
reader1.onerror = reader2.onerror = reject
reader1.readAsArrayBuffer(firstChunk)
reader2.readAsArrayBuffer(lastChunk)
function finish() {
const hash = CryptoJS.SHA256(
CryptoJS.lib.WordArray.create(
concatArrayBuffer(buffer1, buffer2, size)
)
).toString(CryptoJS.enc.Hex)
resolve(hash)
}
})
}
function concatArrayBuffer(buf1, buf2, fileSize) {
const sizeBuffer = new Uint8Array(8)
new DataView(sizeBuffer.buffer).setBigUint64(0, BigInt(fileSize))
const tmp = new Uint8Array(
sizeBuffer.byteLength + buf1.byteLength + buf2.byteLength
)
tmp.set(sizeBuffer, 0)
tmp.set(new Uint8Array(buf1), sizeBuffer.byteLength)
tmp.set(new Uint8Array(buf2), sizeBuffer.byteLength + buf1.byteLength)
return tmp.buffer
}