• 封装一个文件切片上传——上传进度计算


    一、需求

    可实现文件上传,包括但不限于图片、文档、视频、音频等类型,当然具体类型的支持依赖于服务端接口的实现,并且约定好参数。

    前端要做的就是:

    • 文件切片
    • 发送上传请求给服务端
    • 上传进度计算

    服务端要做的就是:

    • 类型检查
    • 接收切片
    • 合并所有切片

    二、具体实现

    1、类型文件mimeType.js

    先导出一个类型文件,后面会用到。

    // mimeType.js
    export default {
        //视频类
        'avi': 'video/x-msvideo',
        'dv': 'video/x-dv',
        'mp4': 'video/mp4',
        'mpeg': 'video/mpeg',
        'mpg': 'video/mpeg',
        'mov': 'video/quicktime',
        'wm': 'video/x-ms-wmv',
        'flv': 'video/x-flv',
        'mkv': 'video/x-matroska',
        'rmvb': 'video/vnd.rn-realmedia-vbr',
        'wmv': 'video/x-ms-wmv',
        'vob': 'video/x-ms-vob',
        //音频类
        'mp3': 'audio/mpeg',
        'mid': 'audio/midi',
        'ogg':  'audio/ogg',
        'mp4a': 'audio/mp4',
        'wav': 'audio/wav',
        'wma': 'audio/x-ms-wma',
        'ape': 'audio/ape',
        'flac': 'audio/flac',
        'aac': 'audio/aac'
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26

    2、文件切片

    定义默认切片大小 chunkSize = 2 * 1024 * 1024
    文件分割使用文件对象 File 的方法 File.prototype.slice

    代码实现:

    // 导入上面的mimeType文件
    import mimeType from './mimeType';
    
    _slice() {
        let totalSize = this.file.size;
        let start = 0;
        let end = start + this.chunkSize;
        let chunks = [];
        let mimetype = 'text/pdf';   // 默认文件类型
        if (this.file.type.includes('video') || this.file.type.includes('audio')) {
            mimetype = this.file.type
        }
        // rmvb文件类型需要处理
        if (this.file.name.endsWith('rmvb') || this.file.name.endsWith('RMVB')) {
            mimetype = mimeType['rmvb']
        }
        // this.file.type不存在时,使用文件后缀名
        if (!this.file.type) {
            let arr = this.file.name.split('.');
            let ext = arr[arr.length - 1].toLowerCase();
            if (/(avi|dv|mp4|mpeg|mpg|mov|wm|vob|flv|mkv|rmvb|wmv|mp3|mid|ogg|mp4a|wav|wma|ape|flac|aac)/.test(ext)) {
                mimetype = mimeType[ext];
            }
        }
        while (start < totalSize) {
            // 根据长度截取每次需要上传的数据
            let chunk = this.file.slice(start, end, mimetype);
            chunks.push(chunk);
            start = end;
            end = start + this.chunkSize;
        }
        return chunks
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33

    以上返回切片后的文件。

    3、发送请求到服务端

    使用 axios,一个基于promise的网络请求库,作用于node.js和浏览器中。
    根据约定好的请求参数,组织数据 FormData

    代码实现

    import axios from 'axios';
    
    _sendRequest(chunkIndex, chunk, params) {
        let formData = new FormData();
        // TODO 根据和服务端同学约定的接口组织请求参数
        formData.append('file', chunk, this.file.name);   // 切片后的文件
        formData.append('dname', this.file.name);   // 文件名
        formData.append('dzchunkindex', chunkIndex);   // 切片索引
        formData.append('dztotalchunkcount', this.chunks.length);   // 所有切片长度
        formData.append('dztotalfilesize', this.file.size);   // 切片大小
    
        if (params && typeof params === 'object') {
            for (let k in params) {
                formData.append(k, params[k]);
            }
        }
        return axios({
            method: this.method,
            baseURL: this.baseURL,
            url: this.url,
            data: formData,
        }).then(res => {
            return res.data
        }).catch(err => {
            return err;
        });
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27

    在以上代码中,在上传切片的同时,也会告诉后端当前上传切片的 chunkIndex,后端接收后,记录该 chunkIndex 以便在合并时知道切片的顺序。

    4、上传处理(上传进度&成功/失败)

    • onStart 上传开始回调
    • onSuccess 上传成功回调
    • onError 上传失败回调
    • onProgress 上传进度回调
    • onCancel 上传取消回调
    async upload(params) {
    	// 上传开始的回调
        this.onStart && this.onStart();
        for (let i = 0; i < this.chunks.length; i++) {
            if (this.cancelled) {
            	// 上传取消处理
                this.onCancel && this.onCancel();
                break;
            }
            console.log(`---------发送文件${this.file.name}${i + 1}个切片-------`);
            let res = await this._sendRequest(i, this.chunks[i], params);
            if (res.success === 1) {
            	// TODO 约定的上传成功的返回参数
                console.log(`---------发送文件${this.file.name}${i + 1}个切片成功-------`);
                // 上传进度计算
                this.onProgress && this.onProgress((((i + 1) / this.chunks.length) * 100).toFixed(1));
                if (i === this.chunks.length - 1) {
                	// 切片是最后一片,上传成功处理
                    this.onSuccess && this.onSuccess(res);
                    break;
                }
            } else {
    			// 上传失败处理
                this.onError && this.onError();
                break;
            }
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28

    当所有切片上传完成(即 当前切片索引 = 所有切片长度-1 )后,不再发送上传请求,即通知后端把所有切片进行合并,最终完成整个上传流程。
    这样,一个基本的文件切片上传就实现了。

  • 相关阅读:
    Flutter全局menu弹框
    Kali Linux渗透测试的艺术
    如何写好软件任务书
    【EI会议征稿】第四届智慧城市工程与公共交通国际学术会议(SCEPT 2024)
    Excel多条件计数——COUNTIFS【获奖情况统计】
    当低代码平台碰到私有化部署,真的就无敌了?
    2023年天津农学院专升本招生专业及报考专业限制范围的通知
    AQS原理及源码解析
    Postgresql源码(64)查询执行——子模块Executor(2)执行前的数据结构和执行过程
    Web前端:JavaScript在网络游戏中的使用
  • 原文地址:https://blog.csdn.net/qq_16525279/article/details/126116186