天天看点

一文讲透antd文件上传,如何实现取消上传功能

一、需求

文件上传有个常见的需求,就是允许用户取消上传文件,特别是在大文件上传时很有必要。

一文讲透antd文件上传,如何实现取消上传功能

在网上找了很多资料,没有现成的代码给我CV(淦)

一文讲透antd文件上传,如何实现取消上传功能

翻了半天,只找到了一个提供思路的帖子,对我还是很有帮助的。

一文讲透antd文件上传,如何实现取消上传功能

二、思路

这就涉及几个问题:

1.如何取消接口请求的问题?

从上面的帖子我得出:

问题1的解决: 使用​

​xhr​

​原生方法​

​abort()​

​可以取消请求,其他​

​xhr​

​库如​

​axios​

​,也可以提供了​

​cancelToken​

​的API取消请求。

这里介绍一下:

1.如何取消请求

1)xhr原生取消请求方法

在​

​XMLHttpRequest​

​对象中可以通过​

​abort​

​方法取消。

let xhr = newXMLHttpRequest();
xhr.open('GET or POST', url);
xhr.send();
// 取消请求使用 xhr.abort()      

2)axios使用canceltoken取消

在axios中,有两种取消当前请求的方式:

第一种通过其内部提供的CancelToken来取消
const CancelToken = axios.CancelToken;
const source = CancelToken.source();
axios.post(url, {data}, {cancelToken: source.token})
// 调用source.cancel()取消请求(可以传参数)      
第二种通过CancelToken的构造函数方式取消请求
letCancelToken = axios.CancelToken;
let cancel = null;
axios.get(url, {
cancelToken: newCancelToken(functionexecutor(c) {
        cancel = c;
    })
})
// 取消请求cancel()      

当然我们可以把​

​cancel​

​函数挂载到window对象上,在需要取消请求的组建或页面中调用​

​window.acncel()​

​,或者绑定到​

​vue​

​组件实例的​

​data​

​里,或是​

​vuex​

​的​

​$store​

​里。

如何批量取消接口

上面的方式一次只能取消一个接口。如果我们一次性要取消多个接口怎么呢?

可以通过传递一个 ​

​executor​

​ 函数到 ​

​CancelToken​

​ 的构造函数创建​

​cancelToken​

​。

​axios​

​有一个​

​CancelToken​

​属性,他是一个类,用于获取取消请求的​

​cancel​

​方法,获取了该方法之后就可以在合适的地方执行cancel()取消请求了。

这种方式比较麻烦,但是可以用于取消多个请求,你可以将​

​c​

​这个取消请求的方法push进一个数组,然后在你需要取消多个请求的时候,循环这个数组,依次执行里面的方法即可取消多个请求。

let arr = [];
    const CancelToken = axios.CancelToken;
     axios.get('http://localhost:6003/axios/4',{
        cancelToken: new CancelToken(function executor(c){
            arr.push(c);
            cancel = c;
        })
      }).then(function(res) {
            console.log(res.data);
          })
      axios.get('http://localhost:3000/axios/3',{
        cancelToken: new CancelToken(function executor(c){
            arr.push(c);
            cancel = c;
        })
      }).then(function(res) {
            console.log(res.data);
          })
      for (let i = 0; i < arr.length; i++) {
        arr[i]('请求取消');
      }      
注意事项:

注意点1:

​cancel​

​取消请求方法,在调用取消请求的时候,可以将取消原因——message字符串传递进去。这样请求在被取消之后,会被catch捕获,你可以在这里将取消原因打印出来或者提示给用户,比如提示用户不要频繁点击发送请求。

const CancelToken = axios.CancelToken;
    const source = CancelToken.source();
     axios.get('http://localhost:6003/axios/4',{
        cancelToken: source.token
      }).then(function(res) {
            console.log(res.data);
          }).catch(function(err) {
            if (axios.isCancel(err)) {
              console.log(err.message);
            }
          })
      source.cancel('不想请求了');      

注意点2:

​get​

​的​

​cancelToken​

​放置在第二个参数的对象里面,​

​post​

​的​

​cancelToken​

​放置在第三个参数对象里面

const CancelToken = axios.CancelToken;
    const source = CancelToken.source();
    axios.post('http://localhost:6003/axios',{
                username: 'lisi',
                content: 123
            },{
                headers:{
                    "Content-Type": "application/json"
                 },
                 cancelToken: source.token
               }

               ).then(function(ret){
                console.log(ret.data);
            })
      source.cancel('不想请求了');      

如何取消接口请求的问题解决了,但此时就出现问题2:

这里通过antd的文件上传组件是封装了请求逻辑的,如何自定义文件上传的接口处理?

问题2的解决: antd文档中提到了,可以使用​

​customRequest​

​这个API,覆盖默认的上传行为,可以自定义自己的上传接口实现。

一文讲透antd文件上传,如何实现取消上传功能

2.antd文件上传组件的方法:customRequest

注意事项:

  1. 定义​

    ​customRequest​

    ​,之前定义​

    ​action​

    ​行为会被覆盖,可以注释掉。
  2. 接口响应后,要处理file上传的成功(​

    ​onSuccess​

    ​)和失败(​

    ​onError​

    ​),还需要改变file的状态(​

    ​status​

    ​),状态有四类:​

    ​uploading​

    ​、​

    ​done​

    ​、​

    ​error​

    ​、​

    ​removed​

  3. 一文讲透antd文件上传,如何实现取消上传功能

​customRequest​

​代码示例如下:

import axios from 'axios'
customRequest (data) {
      let { file, onSuccess, onError } = data
      const formData = new FormData()
      formData.append('file', file)
      formData.append('token', 'aiufpaidfupipiu')//随便写一个token示例
        axios(
        {
          method: 'post',
          url: 'http://localhost:4785/api/values/PostSingle',
          data: formData
        }).then((res) => {
          if (res.data.sccess) {
            file.status = 'done'
            onSuccess(res.data, file)
          }
        }).catch((res) => {
          file.status = 'error'
          onError(res.data, file)
        })
    },      

三、代码实现

OK,现在到我们实现需求的时候,通过定义​

​customRequest​

​来覆写请求逻辑,再通过​

​cancelToken​

​来取消请求,这里我们是批量取消所有的文件上传,所以组件data里用了​

​cancelSourceList​

​存储​

​cancelToken​

​,用于后面弹窗的取消请求实现。代码如下:

HTML:

<template>
<a-upload-dragger name="file" accept=".xls,.xlsx,.csv" :showUploadList="false" :multiple="true"
                            :before-upload="beforeUpload"
                            :customRequest="customRequest" @change="handleImportExcelTemp">
</a-upload-dragger>
<status-modal :visible.sync="fileVisible" :status="fileLoadingStatus" :title="fileModalTitle"
                  @cancel="cancelUpload">
      <div>{{fileModalDescribe}}</div>
</status-modal>
</template>      
export default {
    data() {
      return {
       //存储axios的cancelToken,用于取消请求
        cancelSourceList: [],
        }
     },
     methods: {
      customRequest(options) {
        let { file, onSuccess, onError } = options
        const CancelToken = axios.CancelToken
        const source = CancelToken.source()
        const formData = new FormData()
        formData.append('file', file)
        this.cancelSourceList.push(source)
        
        this.fileVisible = true
        //importExcelFinalUrl是你的接口url
        axios.post(this.importExcelFinalUrl, formData, {
          headers: this.tokenHeader,
          cancelToken: source.token
        }).then((res) => {
            file.status = 'done'
            //这里onSuccess的第一个参数是接口响应结果,会传到change事件中去
            onSuccess(res.data, file)
        }).catch((res) => {
          file.status = 'error'
          onError(res.data, file)
        })
      },
      cancelUpload() {
        this.cancelSourceList.forEach(source => {
          source.cancel('取消请求')
        })
        this.cancelSourceList = []
      },
        // 导入
      handleImportExcelTemp(info) {
        this.$refs.editableTable.getValues((error, values, notPassedMsg) => {
         
          switch (info.file.status) {
            case 'error':
              //info.file.response就是上面onSuccess的第一个参数:接口响应结果
              if (!!!info.file.response) {
                this.$notify['error'].call(this, {
                  key: 'fileUploadFailedNotificationKey',
                  message: '文件上传失败',
                  description: `文件上传失败!`
                })
                this.setFileLoading(false)
                return
              }

              if (info.file.response.status === 500) {
                let data = info.file.response
                const token = Vue.ls.get(ACCESS_TOKEN)
                if (token && data.message.includes('Token失效')) {
                  Modal.error({
                    title: '登录已过期',
                    content: '很抱歉,登录已过期,请重新登录',
                    okText: '重新登录',
                    mask: false,
                    onOk: () => {
                      store.dispatch('Logout').then(() => {
                        Vue.ls.remove(ACCESS_TOKEN)
                        window.location.reload()
                      })
                    }
                  })
                }
              }
              break
            case 'uploading':
              break
            case 'done':
              //处理报错
              if (!info.file.response.success) {
                this.$notify['error'].call(this, {
                  key: 'fileUploadFailedNotificationKey',
                  message: '文件上传失败',
                  description: `${info.file.name} ${info.file.response.message}.`
                })
                this.setFileLoading(false)
                return
              }

              //后续逻辑
              this.setUploadedList(info.file.response.list)
              break
            default:
              break
          }
        })
      },
     }
  }      

四、后续封装