当前位置: 首页 > article >正文

大文件切片上传-vue3.0

大文件切片上传(也称为分片上传)是一种处理大文件上传的有效方法,它通过将大文件分割成多个较小的部分(即切片或分片),然后分别上传这些切片到服务器,最后在服务器上将这些切片合并成原始文件。以下是关于大文件切片上传的详细步骤和注意事项

切片上传核心流程

🌟  确定切片大小

🌟  文件切片(File.slice())

🌟  并发控制 分片上传

🌟  验证文件完整性


基于vue3.0代码实现

<template>
    <el-upload
        v-model:file-list="fileList"
        v-bind="getAttrs($attrs)"
        :on-success="onSuccess"
        :on-error="onError"
        :on-progress="onProgress"
    >
        <template #trigger>
            <el-button type="primary">选择文件</el-button>
        </template>
    </el-upload>
    <el-progress :percentage="percentage ?? 0" v-if="percentage > 0"></el-progress>
</template>

<script setup>
import {computed, reactive} from 'vue';
import SparkMD5 from 'spark-md5';
// import {ApiInitUpload,  ApiUploadChunk, ApiUploadFinish} from './api';
import {CONST} from './config';
defineOptions({
    name: 'PlUpload'
});
const emit = defineEmits(['upldate:modelValue']);

const props = defineProps({
    uploadMode: {
        type: String,
        default: 'default',
    },
    chunkSize: {
        type: Number,
        default: () => 5 * 1024 * 1024,
    },
    data: {
        type: Object,
        default: () => {
            return {};
        },
    },
    limit: {
        type: Number,
    },
    modelValue: {
        type: Array,
    },
    onSuccess: {
        type: Function,
    },
    onError: {
        type: Function,
    },
    onProgress: {
        type: Function,
    },
    onUploadInit: {
        type: Function,
    },
    onUploadFinish: {
        type: Function,
    },
    onUploadChunk: {
        type: Function,
    },
});

const uploadProgress = reactive({
    progress: [],
    total: 0,

});

const fileList = computed({
    get: () =>{
        return props.modelValue;
    },
    set: (val) => {
        console.log('set', val)
        return emit('update:modelValue', val);
    },
});

async function onUpload(response) {
    const chunkList = getChunkList(response.file, props.chunkSize);
    const hash = await getChunkHash(chunkList);
    uploadProgress.total = chunkList.length;

    init(hash);
    uploadChunks(chunkList, hash, response.file.name);
};
function getChunkHash(chunkList) {
    const spark = new SparkMD5();
    const fileReader = new FileReader();
    let currentChunk = 0;

    return new Promise((resolve, reject) => {

        fileReader.readAsArrayBuffer(chunkList[currentChunk]);

        fileReader.onload = (e) => {
            currentChunk++;
            if (currentChunk < chunkList.length) {
                spark.append(e.target.result);
                fileReader.readAsArrayBuffer(chunkList[currentChunk]);
            }
            else {
                resolve(spark.end());
            }
        };
    });

}
function getChunkList(file, chunkSize) {

    const blobSlice = File.prototype.slice || File.prototype.mozSlice || File.prototype.webkitSlice;

    const chunks = Math.ceil(file.size / chunkSize);

    const chunkList = [];

    for(let i = 0; i < chunks; i++) {
        chunkList.push(blobSlice.call(file, i * chunkSize, (i + 1) * chunkSize));
    }

    return chunkList;

}
// 通知开始上传
function init() {
    props?.onUploadInit();
}
// 上传接口
function uploadChunks(chunkList, hash, name) {
    const requestList = chunkList.map((item, index) => {
        const formData = new FormData(); 
        formData.append('file', item);
        formData.append('hash', hash);
        formData.append('file_name', `${index}_${name}`);
        // 添加上传的额外参数
        for (const key in props.data ?? {}) {
            formData.append(key, props.data[key]);
        };
        // return ApiUploadChunk(formData, arg => onUploadProgress(arg, index));
        return props?.onUploadChunk(formData, arg => onUploadProgress(arg, index));
    });
    Promise.all(requestList).then(res => {
        uploadFinish(hash);
    }).catch(err => {
        hubEvent(CONST.ON_ERROR, err);
        throw err;
    });
}

function onUploadProgress(payload, index) {
    uploadProgress.progress[index] = payload.progress;
    hubEvent(CONST.ON_PROGRESS);
};

// 上传完成
async function uploadFinish(hash) {
    try {
        const data = await props?.onUploadFinish({hash});
        hubEvent(CONST.ON_SUCCESS, data);
    }
    catch (err){
        hubEvent(CONST.ON_ERROR, err);
        throw err;
    } 
}

const percentage = computed(() => {
    const {progress, total} = uploadProgress;
    const count = progress?.reduce((total, current) => {
        total+=current;
        return total;
    }, 0);
    return Number(((count / total) * 100)?.toFixed() ?? 0);
});

// utils
function hubEvent(name, data) {
    const event = props[name];
    if (event) {
        event(data);
    }
}

function getAttrs(attrs) {
    const newAttrs = {
        ...attrs,
        [CONST.HTTP_REQUEST]: onUpload,
    }
    if (props.mode !== CONST.MODE_CHUNK) {
        Reflect.deleteProperty(newAttrs, CONST.HTTP_REQUEST);
        Reflect.set(newAttrs, CONST.LIMIT, 1);
    }
    return newAttrs;
}
</script>

配置文件

/**
*config
*/
export const CONST = {
    'MODE_CHUNK': 'chunk',
    'LIMIT': 'limit',
    'HTTP_REQUEST': 'http-request',
    'ON_SUCCESS': 'on-success',
    'ON_ERROR': 'on-error',
    'ON_PROGRESS': 'on-progress',
}

名次解释

SparkMD5:生成文件唯一的hash,用来验证文件的完整性

File.prototype.slice:用于文件进行切片

onUploadInit: 文件初始化调用接口,用于后端创建存放切片文件的文件夹

onUploadChunk:分片上传接口

onUploadFinish:文件上传完成验证完整性的接口

webwork:多线程,提升性能


http://www.kler.cn/a/301355.html

相关文章:

  • UniApp在Vue3的setup语法糖下自定义组件插槽详解
  • 【原创】如何备份和还原Ubuntu系统,非常详细!!
  • 基于Python的仓库管理系统设计与实现
  • Element-ui Select选择器自定义搜索方法
  • Javascript——设计模式(一)
  • 云原生之运维监控实践-使用Telegraf、Prometheus与Grafana实现对InfluxDB服务的监测
  • 如何在VUE3中使用函数式组件
  • ecmascript和javascript的区别?
  • 从底层原理上理解ClickHouse 中的稀疏索引
  • 相互作用先验下的 3D 分子生成扩散模型 - IPDiff 评测
  • Hbase的简单使用示例
  • 在 RT-Thread 上使用单色屏 UI 库 - U8G2
  • 【Shiro】Shiro 的学习教程(四)之 SpringBoot 集成 Shiro 原理
  • 海外云手机是否适合运营TikTok?
  • Kubernetes部署(haproxy+keepalived)高可用环境和办公网络打通
  • Java 21的Preferences API的笔记
  • 分布式中间件-几个常用的消息中间件
  • redis基本数据结构-hash
  • 数据分析-11-时间序列分析的概念任务和主要方法
  • 第R2周:LSTM-火灾温度预测
  • C语言——希尔排序
  • Qt什么时候触发paintEvent事件
  • 【论文笔记】NDT: Neural Data Transformers (NBDT, 2022)
  • 一些深度学习相关指令
  • 【Qt】按钮样式--按钮内部布局(调整按钮文本和图标放置在任意位置)
  • 上海亚商投顾:沪指探底回升 华为产业链午后爆发