一、实现功能点:
1.传输文件进度条展示
2.传输过程中可取消
3.实现多文件上传下载进度展示
二、知识点:
axios的config中封装了onDownloadProgress(下载进度),onUploadProgress(上传进度),cancelToken(取消请求)的方法
1、onDownloadProgress为下载进度方法,在下载文件时被实时调用,返回的progress.loaded为已经下载的文件大小,total在下载时拿不到,只能通过下载前获取文件大小。
注:测试过程中发现,如果下载文件过小,如几十K,onDownloadProgress 不会被调用。此时下载速度非常快,可以在then和catch方法中直接处理进度
axios({
url: 'xxx',
method: 'get',
onDownloadProgress (progress) {
console.log(Math.round(progress.loaded / total * 100) + '%');
}
})
2、onUploadProgress为上传进度的方法,在上传文件时被实时调用,返回的progress.loaded为已经上传的文件大小,progress.total为总文件大小。
注:在下载请求中,也会调用一次onUploadProgress方法。
axios({
url: 'xxx',
method: 'post',
onDownloadProgress (progress) {
console.log(Math.round(progress.loaded / progress.total * 100) + '%');
}
})
3、cancelToken为取消请求方法,每个请求有唯一token,多个请求同时存在时,可以通过token区分关闭哪个请求。
有两种调用方式,我使用的是第二种
方法一
const CancelToken = axios.CancelToken;
const source = CancelToken.source();
axios.get('/url', {
cancelToken: source.token
}).catch(function (thrown) {
if (axios.isCancel(thrown)) {
console.log('Request canceled', thrown.murlessage);
} else {
// handle error
}
});
axios.post('/user/12345', {
name: 'new name'
}, {
cancelToken: source.token
})
// 取消请求时调用此方法
source.cancel('Operation canceled by the user.');
方法二
const CancelToken = axios.CancelToken;
let cancel;
axios.get('/url', {
cancelToken: new CancelToken(function executor(c) {
// An executor function receives a cancel function as a parameter
cancel = c;
})
});
// 取消请求时调用方法
cancel();
三、完整代码(可忽略)
主要记录多个上传下载,对cancelToken和页面显示的处理逻辑,只要了解上传下载和取消请求的原理即可,此部分可忽略。
import React, { Component } from 'react';
import axios from 'axios';
const CancelToken = axios.CancelToken;
// 创建一个数组,存放所有请求的cancelToken的对象
let cancelList = []
export default class BaseCompnent extends Component {
state = {};
// 取消请求时调用的方法
cancelAllAjax = () => {
cancelList.map(e => {
// 执行每个取消请求方法
e()
})
}
// 存放所有的请求句柄
request = {
get: (url, params, options) => {},
post: (url, params, options) => {
return axios.post(url, params, {
cancelToken: new CancelToken((c) => {
// 将所有请求push到cancelList中
cancelList.push(c)
}),
...options,
headers: (Object.assign({}, (options || {}).headers || {}, {'X-Requested-With': 'XMLHttpRequest', 'returnurl': returnUrl, 'token': token})),
}).then(this.resolveFilter, this.rejectFilter);
},
postTest: (url, params, options) => {},
del: (url, params, options) => {};
componentWillUnmount() {
// 组件卸载之后,打断所有未结束得请求,
// 子类要是要使用componentWillUnmount,需要显示的调用super.componentWillUnmount();
this.cancelAllAjax()
}
}
import React, { Component } from 'react';
import { BaseComponent } from 'components';
import { Button, message, Modal, Row, Col, Select, Progress } from 'antd';
const Option = Select.Option;
export default class Operation extends BaseComponent {
constructor(props) {
super(props);
this.state = {
visibleLoad: false, // 上传下载进度条模态框
loadList: [], // 进度条列表
textLoad: true, // 上传下载进度title
}
}
componentWillMount() {}
componentDidMount() {}
// 下载文件流接口
getFileStrem = (fileName, iconType, fileSize, markType) => {
let { loadList } = this.state
loadList.push({
fileName: fileName,
percent: 0,
uid: fileName, // 这里最好不要用文件名做唯一标识,我懒~
status: 'active'
})
this.setState({
visibleLoad: true,
textLoad: false
})
this.request.post('/proxy/file/download', {
responseType: 'blob',
// 处理下载文件进度
onDownloadProgress : progressEvent => {
let percent = progressEvent.loaded / fileSize * 100 | 0
let { loadList } = this.state
// 将每个进度push到loadList中,用于页面展示
loadList.forEach((ele, idex) => {
if (ele.uid == fileName) {
let lc = {
fileName: fileName,
percent: percent,
uid: fileName,
status: 'active'
}
loadList[idex] = lc
this.setState({
loadList
})
}
})
},
}).then((res) => {}).catch((err) => {});
}
// 上传文件
uploadFile = (MultipartFile, type, markType) => {
let { loadList } = this.state
loadList.push({
fileName: MultipartFile.name,
percent: 0,
uid: MultipartFile.uid, // 上传文件可以直接拿到文件的uid,这里直接使用
status: 'active'
})
this.setState({
visibleLoad: true,
textLoad: true
})
var formData = new FormData()
formData.append('fileData', MultipartFile, MultipartFile.name)
this.request.post('/proxy/file/upload', formData, {
headers: {
'Content-Type': 'multipart/form-data',
},
// 处理上传进度
onUploadProgress: progressEvent => {
let percent = progressEvent.loaded / progressEvent.total * 100 | 0
let { loadList } = this.state
// 将每个上传进度push进 loadList,用于页面展示
loadList.forEach((ele, idex) => {
if (ele.uid == MultipartFile.uid) {
let lc = {
fileName: MultipartFile.name,
percent: percent,
uid: MultipartFile.uid,
status: percent === 100 ? 'success' : 'active'
}
loadList[idex] = lc
this.setState({
loadList
})
}
})
}
}).then(res => {}).catch(err => {});
}
handleCancelLoad = () => {
this.setState({
visibleLoad: false,
loadList: []
})
// 取消所有请求
this.cancelAllAjax()
}
render() {
const {visibleLoad, loadList, textLoad} = this.state
return (
<div className="process-content operation-content">
<Modal
title={textLoad ? '正在上传' : '正在下载'}
visible={visibleLoad}
footer={null}
onCancel={this.handleCancelLoad}
>
{
loadList.length && loadList.map((ele, index) => {
return <Row key={index}>
<Col span={5}>{ele.fileName.length <= 21 ? ele.fileName : `${ele.fileName.substring(0, 5)}...${ele.fileName.substring(ele.fileName.length-16,ele.fileName.length)}`}</Col>
<Col span={19}><Progress percent={ele.percent} status={ele.status}/></Col>
</Row>
})
}
<Row style={{borderTop: '1px solid #dddee3', paddingTop: '20px'}}><Col span={2} offset={22}>
<Button onClick={this.handleCancelLoad}>{textLoad ? '取消上传' : '取消下载'}</Button>
</Col></Row>
</Modal>
</div>
</div>
</div>
</div>
)
}
}