当前位置:   article > 正文

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

antd customrequest

一、需求

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

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

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

二、思路

这就涉及几个问题:
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()
  • 1
  • 2
  • 3
  • 4

2)axios使用canceltoken取消

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

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

当然我们可以把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
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
注意事项:
注意点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('不想请求了');
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
注意点2:

getcancelToken放置在第二个参数的对象里面,postcancelToken放置在第三个参数对象里面

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('不想请求了');
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16

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

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

问题2的解决: antd文档中提到了,可以使用customRequest这个API,覆盖默认的上传行为,可以自定义自己的上传接口实现。

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

注意事项:

  1. 定义customRequest,之前定义action行为会被覆盖,可以注释掉。
  2. 接口响应后,要处理file上传的成功(onSuccess)和失败(onError),还需要改变file的状态(status),状态有四类:uploadingdoneerrorremoved

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)
        })
    },
    
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22

三、代码实现

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>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

JS部分:

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
          }
        })
      },
     }
  }
  • 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
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96

四、后续封装

现在只是实现了单独的一个需求,但这个取消接口还是需要封装一下的。
建议看看这篇文章,作者用vuex做了cancelToken的存储,使用更方便。
https://blog.csdn.net/weixin_42206013/article/details/120416765

参考文档:https://blog.csdn.net/qq_37866866/article/details/124837809

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/IT小白/article/detail/339160?site
推荐阅读
相关标签
  

闽ICP备14008679号