赞
踩
文件上传有个常见的需求,就是允许用户取消上传文件,特别是在大文件上传时很有必要。
在网上找了很多资料,没有现成的代码给我CV(淦)
翻了半天,只找到了一个提供思路的帖子,对我还是很有帮助的。
这就涉及几个问题:
1.如何取消接口请求的问题?
从上面的帖子我得出:
问题1的解决: 使用xhr
原生方法abort()
可以取消请求,其他xhr
库如axios
,也可以提供了cancelToken
的API取消请求。
这里介绍一下:
在XMLHttpRequest
对象中可以通过abort
方法取消。
let xhr = newXMLHttpRequest();
xhr.open('GET or POST', url);
xhr.send();
// 取消请求使用 xhr.abort()
在axios中,有两种取消当前请求的方式:
const CancelToken = axios.CancelToken;
const source = CancelToken.source();
axios.post(url, {data}, {cancelToken: source.token})
// 调用source.cancel()取消请求(可以传参数)
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]('请求取消'); }
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('不想请求了');
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,覆盖默认的上传行为,可以自定义自己的上传接口实现。
注意事项:
customRequest
,之前定义action
行为会被覆盖,可以注释掉。onSuccess
)和失败(onError
),还需要改变file的状态(status
),状态有四类:uploading
、done
、error
、removed
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>
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 } }) }, } }
现在只是实现了单独的一个需求,但这个取消接口还是需要封装一下的。
建议看看这篇文章,作者用vuex做了cancelToken的存储,使用更方便。
https://blog.csdn.net/weixin_42206013/article/details/120416765
参考文档:https://blog.csdn.net/qq_37866866/article/details/124837809
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。