Files
hexo-blog/source/_posts/浏览器端crc32文件计算以及分片上传思路.md
2021-03-05 16:01:24 +08:00

3.8 KiB
Raw Permalink Blame History

title, id, cover, categories, tags, date
title id cover categories tags date
浏览器端crc32文件计算以及分片上传思路 362 /images/AAAABSGsf8u86EvIjRhj.jpg
分享
JavaScript
html
crc32
浏览器
分片上传
2020-12-27 15:21:00

大文件上传我们知道需要做MD5计算以及文件分片,由于最近业务业务上的调整,需要把MD5换成crc32,,crc32可以校验文件的完整性,对于业务处理比较方便。这里记录一下解决思路吧。

1. 文件分片

考虑到文件可能很大,出于性能的考虑,优先把文件分片,这里我设置默认5M大小一个分片:

const createFileChunk = (file, size = 5 * 1024 * 1024) => {
    let chunks = [];
    let cur = 0;
    while (cur < file.size) {
        chunks.push(file.slice(cur, cur + size));
        cur += size;
    }
    return chunks;
};

2. 文件crc32计算

首先找一个crc32的库,这里我选了sheetjs的crc32库。 为了提高性能,这种纯计算的功能使用web workers另起一个线程处理。

// crc32_worker.js  

// 引入crc32.js库,importScripts只支持url引入
self.importScripts("https://cdn.bootcdn.net/ajax/libs/crc-32/1.2.0/crc32.js");

// 生成文件 hash
self.onmessage = e => {
    const {
        fileChunks
    } = e.data;
    let crc32 = null;
    let percentage = 0;
    let count = 0;
    const loadNext = index => {
        const reader = new FileReader();
        reader.readAsArrayBuffer(fileChunks[index]);
        reader.onload = e => {
            count++;
            const blob = new Uint8Array(e.target.result);
            if (index === 0) {
                crc32 = self.CRC32.buf(blob);
            } else {
                crc32 = self.CRC32.buf(blob, crc32);
            }

            if (count === fileChunks.length) {
                self.postMessage({
                    percentage: 100,
                    hash: crc32
                });
                self.close();
            } else {
                percentage += 100 / fileChunks.length;
                self.postMessage({
                    percentage
                });
                loadNext(count);
            }
        };
    };
    loadNext(0);
};

web workers调用以及封装:

// 计算文件 crc32(web-worker)参数为上面分片后返回的chunks
const createFileCRC32InWorker = fileChunks => {
    return new Promise(resolve => {
        // 引入上面的worker文件
        const worker = new Worker("./crc32_worker.js");
        worker.postMessage({
            fileChunks
        });
        worker.onmessage = e => {
            // 这边加了进度条 这里的进度条,看需要显示
            const {
                percentage,
                hash
            } = e.data;
            hash && resolve(hash);
        };
    });
};

3. 上传进度本地缓存

计算出的crc32值用来作为判断文件的key,可在localStorage缓存分片上传的进度。

const uploadCache = {
    1259060791: {
        position: [0, 0, 0]
    }
}

如上代码, 1259060791 为文件的crc32值,把它作为key, position 为上传的进度,比如一个15M的文件,会分成3片(默认设置了每片5M),然后根据自己的上传业务,上传完成的分片标记为 1 。

const uploadCache = {
    1259060791: {
        position: [1, 0, 0] // 第一片上传完成后标记为1
    }
}

值得注意的是,通常分片上传都会多个分片并行上传,标记为 1 需要留意 position 的下标跟分片的下标是否一致。

4. 浏览器刷新后获取文件的问题

由于浏览器的安全机制,刷新后无法自动获取到原来的文件,所以需要通过提示去引导用户重现选择该文件,然后才可以断点续传。