一文講透antd文件上傳,如何實現取消上傳功能

語言: CN / TW / HK

一、需求

文件上傳有個常見的需求,就是允許用户取消上傳文件,特別是在大文件上傳時很有必要。

在網上找了很多資料,沒有現成的代碼給我CV(淦)

翻了半天,只找到了一個提供思路的帖子,對我還是很有幫助的。

二、思路

這就涉及幾個問題: 1.如何取消接口請求的問題?

從上面的帖子我得出: 問題1的解決: 使用xhr原生方法abort()可以取消請求,其他xhr庫如axios,也可以提供了cancelToken的API取消請求。 這裏介紹一下:

1.如何取消請求

1)xhr原生取消請求方法

XMLHttpRequest對象中可以通過abort方法取消。

JavaScript 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進一個數組,然後在你需要取消多個請求的時候,循環這個數組,依次執行裏面的方法即可取消多個請求。

JavaScript 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捕獲,你可以在這裏將取消原因打印出來或者提示給用户,比如提示用户不要頻繁點擊發送請求。

JavaScript 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:

getcancelToken放置在第二個參數的對象裏面,postcancelToken放置在第三個參數對象裏面

```JavaScript 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,覆蓋默認的上傳行為,可以自定義自己的上傳接口實現。

2.antd文件上傳組件的方法:customRequest

注意事項: 1. 定義customRequest,之前定義action行為會被覆蓋,可以註釋掉。 2. 接口響應後,要處理file上傳的成功(onSuccess)和失敗(onError),還需要改變file的狀態(status),狀態有四類:uploadingdoneerrorremoved

customRequest代碼示例如下: ```JavaScript 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:

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>

JS部分:

```JavaScript 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
      }
    })
  },
 }

} ```

四、後續封裝

現在只是實現了單獨的一個需求,但這個取消接口還是需要封裝一下的。 建議看看這篇文章,作者用vuex做了cancelToken的存儲,使用更方便。 http://blog.csdn.net/weixin_42206013/article/details/120416765

參考文檔:http://blog.csdn.net/qq_37866866/article/details/124837809