欢迎来到飞鸟慕鱼博客,开始您的技术之旅!
当前位置: 首页知识笔记正文

js formdata 上传多文件,js文件上传fileupload

墨初 知识笔记 127阅读

上传功能需要的一些辅助库
1、使用第三方 vant 组件中的提示做 loading 来区分和提示。
2、大文件上传需要计算 MD5 值使用 spark-md5 库。
3、上传接口自定义的 axios接口使用 Apis[‘方法’] 的方式调用接口此处不是接口封装的重点

功能实现

此处使用 vue 形式编写功能可以提出使用在js方法中注意去掉 ts 内容

<template><input refinputFile typefile hidden changecheckFiles /><van-button classupBut color#BF234D clickonClick>点击上传文件</van-button></template>
<script setup langts>import Apis from /utils/apis;import FileReaderUpoad from /utils/upoad;import { showLoadingToast, showSuccessToast, showFailToast } from vant;/** 创建上传组件实例 */const inputFile  ref<HTMLFieldSetElement | null>(null);/** 创建公共字段集合 */const data  reactive({    loading: false,});// 点击自定义上传按钮调用上传点击事件const onClick  () > {    // 调用 input file 的点击事件    inputFile.value && inputFile.value.click();};// input 获取文件成功后触发的change事件const checkFiles  (e: Event) > {/** 判断拦截是否正在上传 */if (data.loading) return;    data.loading  true;    /** 获取元素内容 */    let event: any  e.target as EventTarget;    /** 获取选中的文件 */    let file  event.files[0];    /** 文件使用的验证ID */    let storageId  ;    let fileName  , suffix  ;    if (file.name) {        let arr  file.name.split(.);        fileName  arr.filter((item: object, index: number) > arr.length - 1 ! index).join(.);        suffix  arr.filter((item: object, index: number) > arr.length - 1  index).join(.);    };    /** 提示 */    showLoadingToast({        message: 正在解析文件,        forbidClick: true,        duration: 0,        overlay: true,    });/** 创建文件分片并发实例(方法参数作用请向下找到自定义封装的分片处理文件中查找) */    let upoad  new FileReaderUpoad({        file,        maxNum: 4,        size: 5242880,        byteLength: file.size,        singleFileSize: 209715200,        /** 单文件上传 */        once: () > {            showLoadingToast({                message: 正在上传中,                forbidClick: true,                duration: 0,                overlay: true,            });            // 单文件上传            let obj: any  {...};            Apis.upLoad(obj).then((res: any) > {                if (res.code  200) {                    data.loading  false;                    showSuccessToast(上传成功);                };            }).catch((err) > {                showFailToast(上传出现问题);                data.loading  false;            });        },        /** 开始分片处理创建分片信息上传分片信息 */        start: (data: any, next: Function) > {            /** 开始创建分片信息上传信息, 具体需要什么信息请联系自己的后端打印 data 数据查找对应字段 */            let obj: any  {            /** 计算好的 md5 值 */                fileMd5: data.md5,                /** 文件总片数 */                sliceTotalChunks: data.total,                /** 其他字段自己添加从 file 文件数据或者 data 中查找 */                ...};            /** 上传分片信息 */            Apis.start(obj).then((res: any) > {                if (res.code  200) {                /** 这里后台可能会返回一个验证 ID 要带在每个分片上传中不需要删除即可 */                    storageId  res.obj.storageId;                    /** 当前分片信息已经上传成功通过调用 next() 函数通知执行下一个逻辑 */                    next();                };            }).catch(() > {                showFailToast(创建上传信息失败);                data.loading  false;            });        },        /** 单个分片上传方法 */        upload: (val: any, next: Function) > {        /** 上传分片参数根据需要添加参数 **/            let obj: any  {            /** 文件名称 */                name: fileName,                /** 当前是第几个分片 */                chunk: val.page,                /** 一共多少分片 */                chunks: val.total,                /** 总文件的md5哈希值 */                md5: val.totalMd5,                /** 总文件的大小 */                size: val.file.size,                /** 验证 ID 值 */                tenantId: storageId,                /** 当前分片的 md5 哈希值 */                currentFileMd5: val.md5,                /** 当前分片文件 */                file: val.blob            };            /** 上传单片文件的接口 */            Apis.burst(obj).then((res: any) > {            /** 上传文件成功直接通过 next() 执行下一步 */                if (res.code  200 && res.success) next();                /** 如果上传失败或者超时则将当前分片传入 next(val),让分片重新进入队列上传分片 */                else next(val);            }).catch(() > {/** 如果上传失败或者超时则将当前分片传入 next(val),让分片重新进入队列上传分片 */                next(val);            });        },        /** 分片上传加载进度返回 */        loading: (val: string) > {            showLoadingToast({                message: 已上传${val}%,                forbidClick: true,                duration: 0,                overlay: true,            });        },        /** 分片上传结束使用当前函数通过接口通知后台上传结束 */        end: (res: any) > {        /** 调用结束接口 */            Apis.end({ tenantId: storageId }).then((res: any) > {                if (res.code  200) {                    showSuccessToast(上传成功);                                    }            }).catch(() > {                showFailToast(上传失败);                data.loading  false;            })        }    });    /** 调用函数开始对文件进行分片处理 */    upoad.uploadStart();};</script>

以下是自定义库的内容可直接使用

import SparkMD5 from spark-md5;interface FileType {    file: File;              // 源文件    size: number;            // 单片大小    once?: Function;         // 单文件上传    maxNum?: number;         // 设置最大并发数    start?: Function;        // 创建开始上传初始化    end?: Function;          // 分片派发完结束返回    upload?: Function;       // 抛出所有拆分的分片    loading?: Function;      // 加载进度    byteLength: number;      // 文件总大小    singleFileSize: number;  // 文件超出多大值做分片处理};export default class FileReaderUpoad {    private option: any  [];       // 所有分片存储    private Setting: FileType;      // 传进来的参数    private totalFilm!: number;     // 总片数    private result: string  ;    // 二进制串    private current: number  0;    // 当前是第几片    private totalMd5: string  ;  // 总文件的md5哈希值    private arrLength: number  0;  // 总长度    constructor(Setting: FileType) {        this.Setting  Setting;    };    // 开始执行    public uploadStart  () > {        let that  this;        // 判断文件是否需要分片        if (this.Setting.file.size > this.Setting.singleFileSize) {            // 将文件转化为二进制串            let fileReader  new FileReader();            fileReader.readAsBinaryString(this.Setting.file);            fileReader.onload  function () {                // 设置并发数                that.Setting.maxNum  that.Setting.maxNum || 1;                // 获取二进制串                that.result  fileReader.result as string;                // 计算总片数                that.totalFilm  Math.ceil(that.Setting.byteLength / that.Setting.size);                // 计算md5值;                that.getDataMd5(that.result).then((res: any) > {                    // 计算成功后将参数传出 start 方法调用上传创建信息                    that.totalMd5  res;                    if (typeof (that.Setting.start)  function) that.Setting.start({ md5: res, total: that.totalFilm }, () > {                        that.current  1;                        that.option  [];                        that.burstParam();                    });                });            };        } else {            typeof this.Setting.once  function && this.Setting.once();        };    };    // 单个分片解析计算以及参数补齐    private burstParam() {        // 如果当前片数小于总片数        if (this.current < this.totalFilm) {            // 计算当前片的起始位置和结束位置            let start  (this.current - 1) * this.Setting.size;            let end  this.current * this.Setting.size;            // 对当前片进行分片处理            this.fileSlice(start, end, (val: string) > {                // 计算当前片的md5值                this.getDataMd5(val).then((res: unknown) > {                    // 将当前片的参数存入option数组中                    this.option.push({                        md5: res,                        chunkFile: val,                        size: val.length,                        page: this.current -1,                        total: this.totalFilm,                        totalMd5: this.totalMd5,                        file: this.Setting.file,                        blob: this.Setting.file.slice(start, end),                    });                    // 当前片数加1                    this.current;                    // 递归调用burstParam函数                    this.burstParam();                });            });        } else {            // 计算最后一片的起始位置和结束位置            let start  (this.current - 1) * this.Setting.size;            let end  this.current * this.Setting.size;            // 对最后一片进行分片处理            this.fileSlice(start, end, (val: string) > {                // 计算最后一片的md5值                this.getDataMd5(val).then((res: unknown) > {                    // 将最后一片的参数存入option数组中                    this.option.push({                        md5: res,                        chunkFile: val,                        size: val.length,                        page: this.current -1,                        total: this.totalFilm,                        totalMd5: this.totalMd5,                        file: this.Setting.file,                        blob: this.Setting.file.slice(start, this.Setting.byteLength),                    });                    // 记录option数组的长度                    this.arrLength  this.option.length;                    // 判断并发数量存不存在并且要大于 0                    if (this.Setting.maxNum && this.Setting.maxNum > 0) {                        // 调用multiRequest函数传入option数组和最大并发数                        this.multiRequest(this.option, this.Setting.maxNum).then((res: any) > {                            // 调用end函数传入结果                            typeof this.Setting.end  function && this.Setting.end(res);                        });                    } else {                        // 并发数不能为零                        new Error(并发数不能为零!);                    };                });            });        };    };    // 分片处理    private fileSlice  (start: number, end: number, callback: Function) > {        callback(this.result.slice(start, end));    };    // 计算md5值    private getDataMd5  (data: string) > {        return new Promise((resolve, reject) > {            if (data) resolve(new SparkMD5().appendBinary(data).end());            else reject(计算失败);        });    };    /**     * 多个请求并发     * param urls 请求地址数组     * param maxNum 最大并发数     * returns Promise     */    private multiRequest  (urls: any, maxNum: number) > {        // 初始化队列        let queue: any  [];        // 克隆urls数组        let urlsClone  [...urls];        // 初始化结果数组        let result  new Array(urls.length);        // 初始化请求是否完成数组        let isDoneArr  new Array(urls.length).fill(0);        // 用于判断请求是否全部完成        let queueLimit  Math.min(maxNum, urls.length);        // 初始化计数器        let index  0;        // 请求返回        const request  (queue: any, url: any) > {            // 如果有上传函数, 调用 upload 函数传参            if (typeof this.Setting.upload  function) this.Setting.upload(url, (val: object) > {                // 保证result结果和 urls 顺序相同                let i  urlsClone.indexOf(url);                result[i]  url;                isDoneArr[i]  1;                // 判断当返回值val有值时则接口请求失败再次添加到请求列表                if(val) {                    // 列表边长说明计算 百分比总数发生变化                    this.arrLength;                    // 添加失败的的接口到请求列表                    urls.push(val);                    // 添加到判断请求是否完毕的列表添加请求次数                    isDoneArr.push(val);                }                // 执行完一个请求后执行出队操作                outLine(queue, url);            })        };        // 进入        const EnterTheTeam  (queue: any  [], url: object) > {            // 请求入队并触发数据请求返回队列长度            let len  queue.push(url);            if (len  this.Setting.maxNum) {                index;                // 计算加载进度                let t  parseInt(String(index / this.arrLength * 100));                // 存在加载进度函数传出当前进度                typeof this.Setting.loading  function && this.Setting.loading(t);            };            request(queue, url);            return len;        };        // 初始化队列请求在此处限制队列长度        while (EnterTheTeam(queue, urls.shift()) < queueLimit) {};                // 设置定义抛出内容        let promise: any  {            resolve: ,            reject: ,        };        // 出队        const outLine  (queue: any  [], url: object) > {            // 一个请求完成出队下一个队列如果存在            queue.splice(queue.indexOf(url), 1);            if (urls.length) EnterTheTeam(queue, urls.shift());            else {                // 判断所有请求完成再 resolve                if (isDoneArr.indexOf(0)  -1) {                    promise.resolve(result);                    typeof this.Setting.loading  function && this.Setting.loading(100);                };            }        };        // 返回所有内容        return new Promise((resolve, reject) > {            return promise.resolve  resolve;        });    };};

标签:
声明:无特别说明,转载请标明本文来源!