一文講透antd文件上傳,如何實現取消上傳功能
一、需求
文件上傳有個常見的需求,就是允許用户取消上傳文件,特別是在大文件上傳時很有必要。
在網上找了很多資料,沒有現成的代碼給我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:
get
的cancelToken
放置在第二個參數的對象裏面,post
的cancelToken
放置在第三個參數對象裏面
```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
),狀態有四類:uploading
、done
、error
、removed
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