使用Crypto.js计算大文件MD5时遇Invalid array length错误的解决咨询
问题:大文件MD5加密触发"Invalid array length"错误
实现文件上传功能时,使用Crypto.js对文件做MD5加密,当文件超过200Mb时,抛出Uncaught RangeError: Invalid array length错误,代码如下:
<script> import CryptoJS from 'crypto-js' const calculateMD5 = (file) => { return new Promise((resolve, reject) => { const fileReader = new FileReader() fileReader.onload = function () { const hash = CryptoJS.MD5(CryptoJS.lib.WordArray.create(fileReader.result)) resolve(hash.toString()) } fileReader.onerror = reject fileReader.readAsArrayBuffer(file) }) } const splitFile = async (event: Event) => { const target = event.target as HTMLInputElement const CHUNK_SIZE = 10 * 1024 * 1024 if (target.files && target.isDefaultNamespace.length > 0) { const file = target.files[0] const fileSize = file.size const md5hash = await calculateMD5(file) console.log(md5hash) const chunks = Math.ceil(fileSize / CHUNK_SIZE) const fileChunks = [] let start = 0 let end = CHUNK_SIZE console.log(end, fileSize) for (let i = 0; i < chunks; i++) { if (end > fileSize) { console.log('if', i) end = fileSize } const chunk = file.slice(start, end) console.log(chunk) fileChunks.push(chunk) start = end end = start + CHUNK_SIZE } console.log(fileChunks) } } </script> <template> <input type="file" @change="splitFile"> </template>
错误原因
- 一次性加载大文件到内存:原代码通过
FileReader.readAsArrayBuffer(file)把整个文件读入内存,再转成CryptoJS的WordArray。当文件超过200Mb时,生成的WordArray内部数组长度会超出JavaScript引擎的限制(V8引擎中,单个数组的最大长度受内存和引擎限制,大文件会导致数组长度超限)。 - V8引擎内存限制:即使系统剩余内存充足,V8引擎对单个对象的内存分配有上限,大文件一次性转成
WordArray会触发这个限制,抛出数组长度无效的错误。
补充参考信息:
- 即使可用堆内存充足,V8引擎也可能因为单个对象的内存上限抛出堆内存不足错误,大文件一次性处理极易触发。
- JavaScript数组和类似对象存在长度上限,超过这个上限会直接抛出
Invalid array length错误。
解决方法:分块计算MD5
不需要一次性加载整个文件,而是分块读取文件内容,逐步更新MD5哈希值,这样内存占用仅为单块大小,避免触发数组长度限制。
修改后的代码如下:
<script> import CryptoJS from 'crypto-js' const calculateMD5ByChunk = (file, chunkSize = 10 * 1024 * 1024) => { return new Promise((resolve, reject) => { const fileReader = new FileReader() let offset = 0 // 初始化MD5哈希对象 let md5Hash = CryptoJS.algo.MD5.create() const processChunk = () => { if (offset >= file.size) { // 所有块处理完成,输出哈希值 resolve(md5Hash.finalize().toString()) return } // 切割当前块 const chunk = file.slice(offset, offset + chunkSize) fileReader.readAsArrayBuffer(chunk) offset += chunkSize } fileReader.onload = function (e) { // 将当前块转成WordArray并更新哈希 const wordArray = CryptoJS.lib.WordArray.create(e.target.result) md5Hash.update(wordArray) // 处理下一块 processChunk() } fileReader.onerror = reject // 启动分块处理 processChunk() }) } const splitFile = async (event: Event) => { const target = event.target as HTMLInputElement const CHUNK_SIZE = 10 * 1024 * 1024 if (target.files && target.files.length > 0) { // 修正原代码中的判断错误 const file = target.files[0] const fileSize = file.size const md5hash = await calculateMD5ByChunk(file, CHUNK_SIZE) console.log(md5hash) const chunks = Math.ceil(fileSize / CHUNK_SIZE) const fileChunks = [] let start = 0 let end = CHUNK_SIZE console.log(end, fileSize) for (let i = 0; i < chunks; i++) { if (end > fileSize) { end = fileSize } const chunk = file.slice(start, end) fileChunks.push(chunk) start = end end = start + CHUNK_SIZE } console.log(fileChunks) } } </script> <template> <input type="file" @change="splitFile"> </template>
关键修改点
- 替换
calculateMD5为calculateMD5ByChunk,分块读取文件并逐步更新MD5哈希。 - 使用
CryptoJS.algo.MD5.create()创建可增量更新的哈希对象,每处理一块就调用update方法更新哈希。 - 修正原代码中的错误:
target.isDefaultNamespace.length改为target.files.length,确保正确判断是否选择了文件。
内容的提问来源于stack exchange,提问作者Iven Liu
相关产品推荐
相关产品推荐

