vue2 elementUi 大文件分片上传

实现思路:获取用户上传的文件——文件流分片处理——监听分片上传完成(上传失败时校验已上传分片,仅重新上传服务端未保存的切片)——合并当前已上传的分片

后端需提供接口:1.检查文件上传情况 2.文件上传 3.文件合并

<template>
    <div class="upload">
        <el-upload class="upload-demo" action="#" :on-change="uploadFile" :show-file-list="true" :file-list="fileList" :auto-upload="false" ref="uploadfile" :limit="1">
            <el-button size="small" type="primary" :loading="loadingFile">上传文件</el-button>
        </el-upload>
    </div>
</template>
 
<script>
import SparkMD5 from "spark-md5";
const chunkSize = 10 * 1024 * 1024;//定义分片的大小 暂定为10M,方便测试
import { message_s, message_w } from '@/methods/element';//引入elementui消息提示框
export default {
    name: '',
    components: {},
    props: {},
    data() {
        return {
            fileList: [],
            loadingFile: false
        }
    },
    watch: {},
    computed: {},
    methods: {
        /**
         * 上传文件
         */
        async uploadFile(File) {
            this.loadingFile = true
            var self = this
            //获取用户选择的文件
            const file = File.raw
            this.currentFile = file
            //文件大小(大于100m再分片哦,否则直接走普通文件上传的逻辑就可以了,这里只实现分片上传逻辑)
            const fileSize = File.size
            // 放入文件列表
            this.fileList = [{ "name": File.name }]
            // 可以设置大于多少兆可以分片上传,否则走普通上传
            if (fileSize <= chunkSize) {
                console.log("上传的文件大于10m才能分片上传")
            }
            //计算当前选择文件需要的分片数量
            const chunkCount = Math.ceil(fileSize / chunkSize)
            console.log("文件大小:", (File.size / 1024 / 1024) + "Mb", "分片数:", chunkCount)
            //获取文件md5
            const fileMd5 = await this.getFileMd5(file, chunkCount);
            console.log("文件md5:", fileMd5)
 
            console.log("向后端请求本次分片上传初始化")
 
            const initUploadParams = {
                "identifier": fileMd5, //文件的md5
                "filename": File.name, //文件名
                "totalChunks": chunkCount, //分片的总数量
            }
            // 调用后端检查文件上传情况接口
            this.service("/upload/checkChunkExist", 'post', initUploadParams, async (res) => {
                //分片上传成功且未合并,那就调用合并接口
                if (res.data.allUploadSuccess === true && res.data.mergeSuccess == false) {
                    console.log("当前文件上传情况:所有分片已在之前上传完成,仅需合并")
                    self.composeFile(fileMd5, File.name, chunkCount)
                    self.storeBucket = ""
                    self.loadingFile = false
                    return false;
                }
                // 所有分片上传成功且合并成功,直接秒传
                if (res.data.allUploadSuccess === true && res.data.mergeSuccess === true) {
                    console.log("当前文件上传情况:秒传")
                    self.loadingFile = false
                    message_s("上传成功")
                    self.fileList = []
                    self.$refs.upload.clearFiles()
                    return false;
                }
                // 获取后端返回的已上传分片数字的数组
                var uploaded = res.data.uploaded
                // 定义分片开始上传的序号
                // 由于是顺序上传,可以判断后端返回的分片数组的长度,为0则说明文件是第一次上传,分片开始序号从0开始
                // 如果分片数组的长度不为0,我们取最后一个序号作为开始序号
                var num = uploaded.length == 0 ? 0 : uploaded[uploaded.length - 1]
                console.log(num, '分片开始序号')
                // 当前为顺序上传方式,若要测试并发上传,请将103 行 await 修饰符删除即可
                // 循环调用上传
                for (var i = num; i < chunkCount; i++) {
                    //分片开始位置
                    let start = i * chunkSize
                    //分片结束位置
                    let end = Math.min(fileSize, start + chunkSize)
                    //取文件指定范围内的byte,从而得到分片数据
                    console.log(File, '0000')
                    let _chunkFile = File.raw.slice(start, end)
                    console.log(_chunkFile)
                    console.log("开始上传第" + i + "个分片")
                    let formdata = new FormData()
                    formdata.append('identifier', fileMd5)
                    formdata.append('filename', File.name)
                    formdata.append('totalChunks', chunkCount)
                    formdata.append('chunkNumber', i)
                    formdata.append('totalSize', fileSize)
                    formdata.append('file', _chunkFile)
 
                    // 通过await实现顺序上传
                    await this.getMethods(formdata)
                }
                // 文件上传完毕,请求后端合并文件并传入参数
                self.composeFile(fileMd5, File.name, chunkCount)
            })
        },
        /**
         * 上传文件方法
         * @param formdata 上传文件的参数
         */
        getMethods(formdata) {
            return new Promise((resolve, reject) => {
                this.service("/upload/chunk", 'post', formdata, (res) => {
                    console.log(res)
                    resolve();
                })
            });
        },
        /**
      * 获取文件MD5
      * @param file
      * @returns {Promise<unknown>}
      */
        getFileMd5(file, chunkCount) {
            return new Promise((resolve, reject) => {
                let blobSlice = File.prototype.slice || File.prototype.mozSlice || File.prototype.webkitSlice;
                let chunks = chunkCount;
                let currentChunk = 0;
                let spark = new SparkMD5.ArrayBuffer();
                let fileReader = new FileReader();
 
                fileReader.onload = function (e) {
                    spark.append(e.target.result);
                    currentChunk++;
                    if (currentChunk < chunks) {
                        loadNext();
                    } else {
                        let md5 = spark.end();
                        resolve(md5);
                    }
                };
                fileReader.onerror = function (e) {
                    reject(e);
                };
                function loadNext() {
                    let start = currentChunk * chunkSize;
                    let end = start + chunkSize;
                    if (end > file.size) {
                        end = file.size;
                    }
                    fileReader.readAsArrayBuffer(blobSlice.call(file, start, end));
                }
                loadNext();
            });
        },
        /**
         * 请求后端合并文件
         * @param fileMd5 文件md5
         * @param fileName 文件名称
         * @param count 文件分片总数
         */
        composeFile(fileMd5, fileName, count) {
            console.log("开始请求后端合并文件")
            var data = {
                "identifier": fileMd5, //文件的md5
                "filename": fileName, //文件名
                "totalChunks": count //分片的总数量
            }
            this.service('/upload/merge', 'post', data, (data) => {
                if (data.successful == true) {
                    message_s(data.message)
                    this.loadingFile = false
                    this.$refs.upload.clearFiles()
                } else {
                    this.loadingFile = false
                    message_w(data.message)
                }
            })
        },
    },
    created() { },
    mounted() { }
}
</script>
<style lang="sass" scoped>
</style>
  • 8
    点赞
  • 33
    收藏
    觉得还不错? 一键收藏
  • 8
    评论
文件分片上传通常需要以下步骤: 1. 切片:将大文件切成多个小文件,每个小文件大小一般在几十 KB 到几 MB 之间。 2. 上传:将每个小文件上传到服务器,可以使用 AJAX 或 WebSocket 等技术实现。 3. 合并:将所有上传的小文件合并成一个完整的大文件。 在 Vue 中,可以使用 element-ui 中的 Upload 组件实现文件上传功能。具体步骤如下: 1. 设置上传地址和上传方式: ```html <el-upload action="/upload" :http-request="uploadFile" :on-exceed="handleExceed" :limit="3" :file-list="fileList"> </el-upload> ``` 其中,`action` 表示上传地址,`http-request` 表示上传方法,`on-exceed` 表示超出文件数量限制时的回调函数,`limit` 表示最多上传文件数量,`file-list` 表示已上传文件列表。 2. 实现上传方法: ```javascript async uploadFile(file) { // 切片文件 const chunks = await this.splitFile(file) // 上传文件 const response = await this.uploadChunks(chunks) // 合并文件 await this.mergeFile(response.data) }, ``` 其中,`splitFile` 方法用于将文件切片,`uploadChunks` 方法用于上传切片文件,`mergeFile` 方法用于合并文件。 3. 实现切片方法: ```javascript async splitFile(file) { const chunkSize = 2 * 1024 * 1024 // 每个切片文件大小为 2MB const chunks = [] let start = 0 let index = 0 while (start < file.size) { const end = Math.min(start + chunkSize, file.size) const chunk = file.slice(start, end) chunks.push({ file: chunk, index: index, name: file.name, size: chunk.size, total: chunks.length, }) start += chunkSize index++ } return chunks }, ``` 该方法将文件切成多个大小相等的切片文件,并返回一个包含切片信息的数组。 4. 实现上传切片文件方法: ```javascript async uploadChunks(chunks) { const responses = [] for (const chunk of chunks) { const formData = new FormData() formData.append('file', chunk.file) formData.append('name', chunk.name) formData.append('index', chunk.index) formData.append('size', chunk.size) formData.append('total', chunk.total) const response = await axios.post('/upload', formData) responses.push(response) } return responses[0] }, ``` 该方法使用 axios 发送 POST 请求,将切片文件和切片信息一起发送到服务器。 5. 实现合并文件方法: ```javascript async mergeFile(data) { const formData = new FormData() formData.append('name', data.name) formData.append('size', data.size) formData.append('total', data.total) const response = await axios.post('/merge', formData) console.log(response.data) }, ``` 该方法将所有切片文件的信息发送到服务器,服务器根据切片信息将所有切片文件合并成一个完整的文件。 以上就是使用 Vue 和 element-ui 实现大文件分片上传的基本步骤。需要注意的是,由于切片和上传都是异步操作,因此需要使用 async/await 或 Promise 等方式来处理。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 8
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值