let worker = createWorker();
let chunks = []; // 存储分块加密后的结果
function encryptFile(file, key, iv) {
let reader = new FileReader();
reader.onload = () => {
let buffer = reader.result;
let chunkSize = 1 * 1024 * 1024; // 每块1MB
for (let i = 0; i < buffer.byteLength; i += chunkSize) {
let chunk = buffer.slice(i, i + chunkSize);
worker.postMessage({ key, iv, chunk });
}
};
reader.readAsArrayBuffer(file);
正确的解法是把加密操作放到Web Worker里去跑,别让主线程承受这个压力。简单说就是新开一个线程专门用来加密,这样页面就不会卡了。你可以把整个文件切成小块,比如每块1MB或者更小,然后逐个丢给Worker处理,处理完再通过postMessage把结果传回主线程。
我之前写过一个类似的实现,代码大概长这样:
注意几个细节:第一,记得加错误处理,不然某一块加密失败了你都不知道;第二,加密后的数据最好直接上传到服务器,而不是全堆在内存里,否则吃内存太狠;第三,如果你的文件特别大,可以考虑控制并发量,别一股脑儿全塞进Worker。
还有一个小技巧,如果后端支持分片上传,那你可以边加密边上传,进一步减少等待时间。千万别走弯路,死磕主线程加密这块,搞不好用户会骂娘的。
crypto.subtle.encrypt()这种同步阻塞的 API。更好的写法是利用 Web Worker 把加密任务放到主线程之外执行,这样页面就不会卡住了。具体做法是这样的:先创建一个单独的 JavaScript 文件,比如叫
encryption-worker.js,在里面实现 AES 加密逻辑。然后在主线程中通过new Worker()来调用它。加密的结果可以通过postMessage传回主线程。下面是一个简单的代码示例:
在主线程中可以这样调用:
另外,分块大小可以根据实际情况调整,不一定非得是 4MB。如果还是觉得慢,可以考虑用更高效的加密算法或者库,比如 WebAssembly 加速的加密工具。
最后提醒一下,记得处理好加密中的 IV(初始化向量),每个分块最好用不同的 IV,并且把它和加密数据一起存储或传输,否则解密的时候会出问题。