天天看點

一文講透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
          }
        })
      },
     }
  }      

四、後續封裝