Skip to content

文件上传

更新: 2025/3/12 17:59:43 字数: 0 字

文件切片

  在做 知识图谱 时,需要上传对应的音乐的视频文件,但是文件太大了,大部分都在 500MB 以上,所以需要分片上传。

ts
import { cutFile } from'./cutFile.js';

const inpFile = document.querySelector('input[type="file"]');

// 选择文件后会触发的 onchange 事件
inpFile.onchange = async(e) =>{
    const file = e.target.files[o]; 
    console.time('cutFile');
    const chunks= await cutFile(file);
    console.timeEnd('cutFile');
    console.log(chunks);
};

  cutFile 函数主要来将文件进行分片,返回的是一个数组,数组中的每个元素都是一个分片,分片大小为 5MB

ts
// cutFile.js
const CHUNK_SIZE = 1024 * 1024 * 5;
const THREAD_COUNT = navigator.hardwareConcurrency || 4;

// 分片是耗时任务,我们可以将其放到 worker 中执行,这样不会阻塞主线程
/**
 * 将文件进行分片
 * @param file
 */
export async function cutFile(file: any) {
    return new Promise((resolve) => {
        let result: any = []
        const chunkCount = Math.ceil(file.size / CHUNK_SIZE);
        // 每一个线程需要处理的分片数量
        const workerChunksCount = Math.ceil(chunkCount / THREAD_COUNT); 
        let finishCount = 0;
        for (let i = 0; i < THREAD_COUNT; i++) {
            const worker = new Worker("../worker.js", {
                type: "module",
            })
            // 线程开始索引
            const startIndex = i * workerChunksCount;
            const endIndex = startIndex + workerChunksCount;
            worker.postMessage({
                file,
                chunkSize: CHUNK_SIZE,
                startIndex: startIndex,
                endIndex: endIndex,
            })
            worker.onmessage = (e) => {
                for (let i = startIndex; i < endIndex; i++){
                    return[i] = e.data[i - startIndex];
                }
                worker.terminate()
                finishCount++;
                if(finishCount === THREAD_COUNT){
                    resolve(result)
                }
            }
        }
    })
}

  worker.js 文件中,主要就是将文件进行分片,并且计算出对应的 MD5 值,然后将分片和 MD5 值一起返回。

ts
import {createChunk} from "./createChunk.ts";

onmessage = async (e) => {
    const {file, startIndex, endIndex, chunkSize} = e.data
    const prams = []
    for (let i = startIndex; i <= endIndex; i++) {
        prams.push(createChunk(file, i, chunkSize))
    }
    const result = await Promise.all(prams)
    postMessage(result)
}
ts
import SparkMD5 from "./sparkmd5.js"
// SparkMD5 可以去 github 上下载

/**
 * 创建分片
 * @param file 传入的文件
 * @param index 第几个分片
 * @param chunkSize 分片大小
 */
export function createChunk(file: any, index: any, chunkSize: any) {
    return new Promise((resolve) => {
        const start = index * chunkSize; // 开始位置
        const end = start + chunkSize // 结束位置
        const spark: any = new SparkMD5.ArrayBuffer(); // 创建md5对象
        const fileReader = new FileReader(); // 创建文件读取对象
        fileReader.onload = function (e: any) {
            spark.append(e.target.result); // 将文件内容添加到md5对象中
            resolve({
                start,
                end,
                index,
                hash: spark.end(), // 获取md5值
            });
        }
        fileReader.readAsArrayBuffer(file.slice(start, end));
    })
}

web work 使用

  web worker 是一种异步执行代码的方式,它可以在浏览器中运行,并且不会阻塞主线程,从而提高性能。

WebWorkhtml 中使用方法:

html
<!DOCTYPE html>
<html lang="en">
<head>
    <title>WebWork</title>
</head>
<body> </body>
<script>
    // 直接创建 work.js 文件
    var work = new Worker('./work.js')
    work.postMessage(1000000000)
    work.onmessage = function(res){
        console.log("累加结果 : ",res.data)
    }
</script>
</html>
js
onmessage = function (e) {
    let sum = 0, num = e.data;
    for (var i = 1; i <= num; i++) {
        sum += i
    }
    postMessage(sum);
}

在 react 中使用 web worker

ts
// index.tsx
const worker = new Worker('./worker.ts')

  web worker 需要指定一个脚本的 URI,而在 React 的实际项目中,我们一般都会和打包工具一起使用(比如:webpack),这时如果直接引用文件,就会发生错误。

  如果使用 webpack ,可以通过 worker-loader 来解决,配置如下:

js
{
  module: {
    rules: [
      {
           test: /\.worker\.ts$/,
           use: [
                {
                    loader: 'worker-loader',
                    options: {
                        inline: 'fallback',
                    },
                },
                {
                    loader: 'ts-loader',
                },
           ],
      }
    ]
  }
}

  worker-loader 会把文件加载为 web worker,我们只需要把文件名命名为 /\.worker\.ts$/ 格式,比如 msg.worker.ts

道友再会.