实现上传下载进度条和取消ajax请求

一、实现功能点:

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>
        )
    }
}

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,294评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,493评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,790评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,595评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,718评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,906评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,053评论 3 410
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,797评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,250评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,570评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,711评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,388评论 4 332
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,018评论 3 316
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,796评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,023评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,461评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,595评论 2 350

推荐阅读更多精彩内容