1

一、前言

在项目中,很多情况下我们都会用到文件上传,但是当某个文件太大,或者不小心选错了文件时,我们就需要终止当前的请求,重新上传文件。同时也能提高网站性能,去除多余请求。

二、代码

现在我们上传文件,很多都会上传到OSS,现在以阿里云的OSS上传为例(仅为前端代码,Vue实例):

uplodaFile组件封装

<template>
    <div :class="$style.uploadPack">
        <input type="file" @change="$_onUpload" />
        <slot /> 
    </div>
</template>

<script>
import { commonService } from '@/common/service'    // 封装的接口请求,根据项目情况而定
import axios from 'axios'

export default {
    methods: {
        async $_onUpload(e) {
            this.$emit('input', { status: 'begin', data: null })
            const files = e.target.files
            let formData = new FormData()
            const fileName = files[0].name
            /**
            getOssToken 返回的数据格式
             {
                expire: 1607499265904
                params: { 
                    signature: "1",
                    OSSAccessKeyId: "1",
                    key: "1",
                    policy: "1",
                    success_action_status: "200",
                    x-oss-security-token: "1"
                },
                url: "http://xxx.aliyuncs.com"
             }
            */
            const { data } = await commonService.getOssToken({ filename: fileName })    // 获取阿里云oss返回的数据
            Object.keys(data.result.params).forEach(key => {
                formData.append(key, data.result.params[key])
            })
            formData.append('file', files[0])
            const self = this
            try {
                 /**
                 * 注意此处接口没有数据返回 只有状态
                 * 成功后需要自己根据 getOssToken 接口返回的数据组装返回
                 */
                await axios.post(data.result.url, formData, {
                    cancelToken: new axios.CancelToken(function executor(c) {
                        self.source = c
                    }),
                })
                /**
                 * 通过同步数据给父组件
                 */
                this.$emit('input', { status: 'end', data: { file_name: fileName, url: data.result.url + '/' +  data.result.params['key'] } })
                /**
                 * 上传成功后清空表单,避免重名文件不再次执行change事件
                 */
                e.target.value = ''
            } catch (err) {
                /**
                 * 是否是手动终止取消 导致的失败
                 */
                if (axios.isCancel(err)) {
                    this.$message.error(err.message)
                } else {
                    this.$message.error('上传失败')
                }
                this.$emit('input', { status: 'end', data: {} })
                e.target.value = ''
            }
        },
        /**
        * 终止上传接口请求
        */
        cancelRequest() {
            if (typeof this.source === 'function') {
                this.source('取消文件上传')
            }
        },
    },
}
</script>
<style lang="scss" module>
.uploadPack {
    position: relative;
    overflow: hidden;
    input {
        position: absolute;
         z-index: 1;
         width: 100%;
         height: 100%;
         opacity: 0;
    }
}
</style>

组件使用

<template>
    <div>
        <uploadPack v-model="packInfo" ref="uploadPack">
            <a-button :loading="loading" type="primary">上传</a-button>
        </uploadPack>
        <a-button @click="cancelRequest"></a-button>
    </div>
<template>

<script>
export default {
    data() {
        return {
            loading: false,
            fileData: {},
        }
    },
    watch: {
        /**
         * 监听文件上传的数据变化
         */
        packInfo(newValue) {
            if (newValue.status === 'begin') {
                this.loading = true
            } else if (newValue.status === 'end') {
                this.loading = false
                this.fileData = newValue.data
            }
        }
    },
    methods: {
        /**
         * 终止上传
         */
        cancelRequest() {
            this.$refs['uploadPack'] && this.$refs['uploadPack'].cancelRequest()
        }
    }
}
<script>

三、结语

使用场景:
  • 页面中新增弹层中有需要上传文件,点关闭弹层按钮时需要终止当前请求。
  • 文件上传错误,需要点击取消,重新选择。
  • 多次请求时,终止上一次请求,例如搜索框,每输入一个字符都要发送一次请求,但输入过快的时候其实前面的请求并没有必要真的发送出去。

前端老鹰
128 声望4 粉丝

欢迎大家访问我的github:[链接]