美文网首页
ElementUI upload 文件自定义上传 和 文件自定义

ElementUI upload 文件自定义上传 和 文件自定义

作者: Cherry丶小丸子 | 来源:发表于2021-02-09 00:09 被阅读0次
<div id="app">
    <el-upload
        class="upload-demo"
        multiple
        limit="5"
        :file-list="fileList" // file-list内的变量是只读的,不能随意更改其值,否则会出现不可思议的bug,此属性一般作为数据回显使用

        :on-exceed="handleExceed"
        :on-change="handleChange"
        :before-upload="handleBeforeUpload"

        :http-request="uploadCustomRequest" // 自定义上传
        :http-request="uploadCustomRequestBlock" // 自定义分块上传

        :on-progress="handleProgress"
        :on-success="handleSuccess"
        :on-error="handleError"

        :on-preview="handlePreview"

        :before-remove="handleBeforeRemove"
        :on-remove="handleRemove">

        <el-button size="small" type="primary">点击上传</el-button>
        <div slot="tip" class="el-upload__tip">只能上传jpg/png文件,且不超过500kb</div>
    </el-upload>
</div>
<script>
    import axios from "axios"; // 引入axios,以便获取 axios 取消请求对象
    export default{
        data(){
            return {
                fileList: [], // 数组对象字段名分别为 name、url
                fileListSrc: [], // 上传成功的文件集合
                cancelToken: [], // 每个上传文件的请求的 cancelToken
                messageInstance: null, // message提示信息的实例
                allFileUploadStatus: true, // 全部文件 是否 全部上传成功
            }
        },
        methods:{
            /**
             * 文件超出个数限制时的钩子
             * @param {Object} files
             * @param {Object} fileList
             */
            handleExceed(files, fileList){
                this.$message.warning(`当前限制选择 5个文件,本次选择了 ${files.length} 个文件,共选择了 ${files.length + fileList.length} 个文件`);
            },
            /**
             * 文件状态改变时的钩子,添加文件、上传成功和上传失败时都会被调用
             * @param {Object} file
             * @param {Object} fileList
             */
            handleChange(file, fileList){
                // 是否全部文件上传成功
                let flag = true;
                fileList.map((item, index) => {
                    if(item.status != 'success'){
                        flag = false;
                    }
                })
                
                // true: 全部文件上传成功, false: 任何一个文件上传未成功
                this.allFileUploadStatus = flag ? true : false;
                
                // 全部文件上传成功,关闭 “上传中” 提示
                if(flag){
                    this.messageInstance.close();
                    this.messageInstance = null;
                }
            },
            /**
             * 上传文件之前的钩子,参数为上传的文件,若返回 false 或者返回 Promise 且被 reject,则停止上传
             * @param {Object} file
             */
            handleBeforeUpload(file){
                // 验证文件类型
                const isAcceptType = /\.(txt|doc|docx|xls|xlsx|wps|pdf|jar|war|zip|rar|tar|gz)$/.test(file.name.substring(file.name.lastIndexOf('.')));
                // 验证文件大小(1G),file.size单位为B,除以1024变为KB,再除以1024变为MB
                const isAcceptSize = file.size / 1024 / 1024 <= 1024;
                
                if (!isAcceptType) {
                    this.$message.warning({
                        dangerouslyUseHTMLString: true,
                        message: "<p style='white-space: nowrap;'>只能上传.txt、.doc、.docx、.xls,.xlsx、.wps、.pdf、.jar、.war、.zip、.rar、.tar、.gz格式的文件!</p>"
                    });
                } else if (!isAcceptSize) {
                    this.$message.warning({
                        message: "上传的文件过大!"
                    });
                }

                // 如果符合文件类型及大小,则验证文件名是否重名
                if(isAcceptType && isAcceptSize){
                    this.$axios.checkUploadFileDuplicateName({
                        "fileName": info.file.name //文件名称
                    }).then(res => {
                        if(res.code == 0){
                            return true; // 允许上传
                        }else{
                            this.$message.error("文件名重复,请确认该文件是否已存在");
                            return false; // 不允许上传
                        }
                    })
                }

                // return isAcceptType && isAcceptSize; // 如果不需要验证重名,则使用此代码
            },
            /**
             * 自定义上传
             * @param {Object} info
             */
            uploadCustomRequest(info){
                const CancelToken = axios.CancelToken; // 获取 axios 取消请求对象

                // 请求配置
                let config = {
                    url: "/api/door/upload", // url、method、data属性可以不写在此处,根据自己的 axios 封装方案
                    method: "post",
                    data: {
                        file: info.file, // 上传的文件
                        hash: this.$uuid.v1(), // 生成的uuid(安装vue-uuid插件)
                        chunkNumber: 0
                    },
                    timeout: 0,
                    headers: {
                        'Content-Type': 'multipart/form-data',
                    },
                    cancelToken: new CancelToken(c => {
                        // this.cancelToken = c; // 单文件上传
                        this.cancelToken.push({ // 多文件上传
                            uid: info.file.uid,
                            cancelToken: c
                        })
                    }),
                    // 上传处理进度事件
                    onUploadProgress: progressEvent => {
                        // 获取当前上传进度
                        let complete = (progressEvent.loaded / progressEvent.total * 100 | 0);
                        let progress = { percent: complete };
                        info.onProgress(progress); // 调用 onProgress
                    }
                };

                // 将 config.data 转为 formData
                let formData = new FormData();
                Object.keys(config.data).forEach(key => {
                    const value = config.data[key];
                    formData.append(key, value);
                });
                config.data = formData; // 最后将 formData 重新赋值给 data ,接口调用参数

                // 打开 “上传中” 提示
                if(this.messageInstance == null){
                    this.messageInstance = this.$message({
                        type: 'info',
                        message: '正在上传中,请稍后......,上传过程中,请勿关闭弹框或浏览器!',
                        duration: 0
                    })
                }

                // 调用接口-----文件上传
                this.$axios.upload(config).then(result => {
                    if (result.code === 0) {
                        info.onSuccess(result); // 解决一直loading情况,调用 onSuccess
                    } else {
                        info.onError(); // 调用 onError
                    }
                }).catch(err =>{
                     info.onError(); // 调用 onError
                });
            },
            /**
             * 文件自定义分块上传
             * @param {Object} info
             */
            uploadCustomRequestBlock(info){
                const CancelToken = axios.CancelToken; // 获取 axios 取消请求对象

                let MAX_FILE_SIZE = 5 * 1024 * 1024; // 设置分割大小为(5242880 B)==(5M)
                let index = 0; // 分割数
                let file = info.file; // 获取文件
                let fileSize = file.size; // 获取文件大小
                let start = 0; // 文件切割起始位置
                let end = 0; // 文件切割结束位置
                let uuid = this.$uuid.v1(); // 创建uuid
                let cutNum = Math.ceil(file.size / MAX_FILE_SIZE); // 文件分割的个数

                // 文件上传进度回调函数
                let handleUploadProgress = (progressEvent) => {
                    // 这里的上传百分比是切割块的百分比,所以要结合整个文件的大小算上传百分比
                    let percent = parseInt((progressEvent.loaded + start) / fileSize * 100);
                    // 如果格式化之后超过了100%之后,直接变成100%
                    if (percent > 100) {
                        percent = 100;
                    }
                    let progress = { percent: percent };
                    info.onProgress(progress); // 调用 onProgress
                }
                // 上传成功的回调函数
                let handleUploadSuccess = (res) => {
                    if (res.code == 0) {
                        // 调用全部分块合并接口
                        this.$axios.uploadBlockMerge({
                            data:{
                                "projectId": "0", //项目主键
                                "hash": uuid, //唯一值
                                "filename": file.name, //文件
                                "dataType": "3", //1 文档数据 2 空间数据 3 文件
                                "total": index + 1, // 文件总数
                            }
                        }).then(message => {
                            if (message.code == 0) {
                                info.onSuccess(message); // 解决一直loading情况,调用onSuccess
                            } else {
                                info.onError(); // 调用 onError
                            }
                        })
                    } else {
                        info.onError(); // 调用 onError
                    }
                }
                // 上传失败的回调函数
                let handleUploadError = (err) => {
                    info.onError(); // 调用 onError
                }
                // 文件上传切割方法
                let uploadFileByBlock = () => {
                    if (start + MAX_FILE_SIZE >= fileSize) { // 判断切割的文件是否大于总文件大小
                        end = fileSize; // 如果大于总文件大小,将总文件大小直接作为一块
                    }else{
                        end = start + MAX_FILE_SIZE; // 如果小于总文件大小,
                    }
                    const fileBlock = file.slice(start, end); // 文件切割
                    const formData = new FormData(); // 创建FormData对象
                    // // 数据内容
                    // formData.append('file', fileBlock);
                    // // 第几块数据
                    // formData.append('index', index);
                    // // 上传数据大小
                    // formData.append('size', end - start);

                    // 请求配置
                    let config = {
                        data: {
                            file: fileBlock, // 上传的文件
                            hash: uuid, // 生成的uuid
                            chunkNumber: index, // 第几块
                            dataType: '3', // 1 文档数据 2 空间数据 3 文件
                        },
                        timeout: 0,
                        headers: {
                            'Content-Type': 'multipart/form-data',
                        },
                        cancelToken: new CancelToken(c => {
                            // this.cancelToken = c; // 单文件上传
                            this.cancelToken.push({ // 多文件上传
                                uid: info.file.uid,
                                cancelToken: c
                            })
                        }),
                        // 上传处理进度事件
                        onUploadProgress: handleUploadProgress
                    };

                    Object.keys(config.data).forEach(key => {
                        const value = config.data[key];
                        formData.append(key, value);
                    });
                    config.data = formData; // 最后将 formData 重新赋值给 data ,接口调用参数

                    // 打开 “上传中” 提示
                    if(this.messageInstance == null){
                        this.messageInstance = this.$message({
                            type: 'info',
                            message: '正在上传中,请稍后......,上传过程中,请勿关闭弹框或浏览器!',
                            duration: 0
                        })
                    }

                    // 调用接口-----文件分割上传
                    this.$axios.uploadBlock(config).then(res => {
                        // 判断当前分割是否是最后一块
                        if (start + MAX_FILE_SIZE >= fileSize) {
                            handleUploadSuccess(res); // 分割全部上传
                        } else {
                            start += MAX_FILE_SIZE;
                            index += 1;
                            uploadFileByBlock();
                        }
                    }).catch(err =>{
                        handleUploadError(err);
                    });
                }

                uploadFileByBlock(); // 调用文件上传切割方法
            },
            /**
             * 文件上传时的钩子
             * @param {Object} event
             * @param {Object} file
             * @param {Object} fileList
             */
            handleProgress(event, file, fileList){
                console.log(event)
            },
            /**
             * 文件上传成功时的钩子
             * @param {Object} response
             * @param {Object} file
             * @param {Object} fileList
             */
            handleSuccess(response, file, fileList){
                this.$message.success(`文件 ${file.name} 上传成功!`);
                // 上传成功的文件插入到 fileListSrc 中
                this.fileListSrc.push(file);

                // 删除 axios 取消请求对象
                for(let i = 0; i < this.cancelToken.length; i++){
                    if(this.cancelToken[i].uid == file.uid){
                        this.cancelToken.splice(i--, 1);
                    }
                }
            },
            /**
             * 文件上传失败时的钩子
             * @param {Object} err
             * @param {Object} file
             * @param {Object} fileList
             */
            handleError(err, file, fileList){
                this.$message.error(`文件 ${file.name} 上传失败!`);

                // 删除 axios 取消请求对象
                for(let i = 0; i < this.cancelToken.length; i++){
                    if(this.cancelToken[i].uid == file.uid){
                        this.cancelToken.splice(i--, 1);
                    }
                }
            },
            /**
             * 点击文件列表中已上传的文件时的钩子
             * @param {Object} file
             */
            handlePreview(file){
                
            },
            /**
             * 删除文件之前的钩子,参数为上传的文件和文件列表,若返回 false 或者返回 Promise 且被 reject,则停止删除
             * @param {Object} file
             * @param {Object} fileList
             */
            handleBeforeRemove(file, fileList){
                if (file && file.status === 'success') { // 阻止 before-upload 之后触发 before-remove 以及 on-remove 方法
                    return this.$confirm(`确定移除 ${file.name}?`, '提示', {
                        message: '此操作将永久删除此文件,是否继续', // MessageBox 消息正文内容
                        customClass: 'deleteAppMsgbox',
                        showConfirmButton: true, // 是否显示确定按钮
                        showCancelButton: true, // 是否显示取消按钮
                        confirmButtonText: '确定', // 确定按钮的文本内容
                        cancelButtonText: '取消', // 取消按钮的文本内容
                        confirmButtonClass: 'sureBtn', // 确定按钮的自定义类名
                        cancelButtonClass: 'cancelBtn', // 取消按钮的自定义类名
                        closeOnClickModal: false, // 是否可通过点击遮罩关闭 MessageBox
                        closeOnPressEscape: false, // 是否可通过按下 ESC 键关闭 MessageBox
                        type: 'warning'
                    }).then(() => {
                        this.$axios.questionBank.deleteProblemFile({
                            params: {
                                filePath: file.response.data.data.filePath[0]
                            }
                        }).then(res => {
                            if(res.data.data == '删除成功'){
                                this.$message.success("删除成功");
                            }else{
                                this.$message.error("删除失败");
                                reject();
                            }
                        }).catch(err => {
                            this.$message.error("删除失败");
                            reject();
                        })
                    }).catch(() => {
                        this.$message({
                            type: 'info',
                            message: '已取消删除'
                        });
                        reject();
                    })
                }else if(file && file.status === 'uploading'){ // 当文件状态为 “上传中” ,则取消请求
                    for(let i = 0; i < this.cancelToken.length; i++){
                        if(this.cancelToken[i].uid == file.uid){
                            this.cancelToken[i].cancelToken(); // 取消上传
                            this.cancelToken.splice(i--, 1); // 删除 axios 取消请求对象
                        }
                    }
                }
            },
            /**
             * 文件列表移除文件时的钩子
             * @param {Object} file
             * @param {Object} fileList
             */
            handleRemove(file, fileList){
                if (file && file.status === "success") { // 阻止 before-upload 之后触发 before-remove 以及 on-remove 方法
                    // 是否全部删除
                    if(fileList.length == 0){
                        // 更改全部文件状态为:“上传成功”
                        this.allFileUploadStatus = true;
                    }
                                
                    // 是否全部文件上传成功
                    let flag = true;
                    fileList.map((item, index) => {
                        if(item.status != 'success'){
                            flag = false;
                        }
                    })
                    // 全部文件上传成功,关闭 “上传中” 提示
                    if(flag){
                        if(this.messageInstance != null){
                            this.messageInstance.close();
                            this.messageInstance = null;
                        }               
                    }
                    // 删除文件
                    for(let i = 0; i < this.fileListSrc.length; i++){
                        if(this.fileListSrc[i].uid == file.uid){
                            this.fileListSrc.splice(i--, 1);
                        }
                    }
                }
            },
        }
    }
</script>

注意:如果文件上传后,有提交需求,记得将 this.fileList 和 this.fileListSrc 合并

uploadCustomRequest 参数

uploadingFileInfo.png

已上传的文件

upLoadedFileInfo.png

相关文章

网友评论

      本文标题:ElementUI upload 文件自定义上传 和 文件自定义

      本文链接:https://www.haomeiwen.com/subject/yrbztltx.html