ajax上传和下载文件,jq axios和原生ajax实现文件上传和下载,ajax下载二进制文件流

随笔 专栏收录该内容
28 篇文章 0 订阅

遇到了一个上传文件和下载文件的业务,利用ajax实现,上传单文件整体上传,不进行分片上传相对简单,这里也暂不讨论大文件分片上传的情况,后面可能会写这个。下载文件如果后端返回链接可以直接赋值给a的href点击或者window.location.href下载,但是后端如果返回的是文件流则需要进行处理再下载。

这里都会用到FormData构造方法,先了解一下FormData:

FormData 接口提供了一种表示表单数据的键值对的构造方式,经过它的数据可以使用 XMLHttpRequest.send() 方法送出,本接口和此方法都相当简单直接。FormData()创建一个新的 FormData 对象。使用formData的append方法将新值追加到FormData对象内的现有键上,或者添加该键(如果该键尚不存在)。

上传文件:jqAjax方法

//上传和文件
function upLoadFile({ url, name = '', data = '', } = {}) {
    if (!url) {
        return;
    }
    let FD = new FormData();
    FD.append(name, data);
    return new Promise((resolve, reject) => {
        $.ajax({
            url: url,
            type: 'POST',
            data: FD,
            processData: false, //很重要,告诉jq不要对data数据进行处理
            contentType: false, //很重要,指定为false才能形成正确的content-type
            success: function(data) {
                resolve(data);
            }
        })
    });
}

上传文件:原生实现

function upLoadFile({ url, name = '', data = '', } = {}) {
    if (!url) {
        throw new Error('the url para is invalid');
    }
    let formData = new FormData();
    formData.append(name, data);

    return new Promise((resolve, reject) => {
        const xhr = new XMLHttpRequest();
        xhr.open('POST', url, true);
        xhr.onreadystatechange = function() {
            if (xhr.readyState === 4 && xhr.status === 200) {
                resolve(xhr.response);
            }
        }

        xhr.send(formData);
    })

}

下载文件的时候,如果后端直接返回的是一个URL链接,那么你直接可以把这个链接赋给a标签的href然后在点击就可以下载了,或者把这个链接赋值给window.location.href即可立即下载。

但是如果后端返回给前端的是二进制格式的文件流,那么前端就需要把这个二进制流进行转换生成URL对象,然后赋值给a标签进行点击触发下载。

URL.createObjectURL() 静态方法会创建一个 DOMString,其中包含一个表示参数中给出的对象的URL。这个 URL 的生命周期和创建它的窗口中的 document 绑定。这个新的URL 对象表示指定的 File 对象或 Blob 对象。

下载文件:原生实现:

//下载文件
function downLoadFile({ url, dataSend = {}, fileName = 'name' } = {}) {
    if (!url) {
        throw Error('ajaxData function need a valid url');
    }

    let formData = new FormData();
    for (para in dataSend) {
        formData.append(para, dataSend[para]);
    }

    return new Promise((resolve, reject) => {
        const xhr = new XMLHttpRequest();
        xhr.open('POST', url, true);
        //处理二进制文件流
        xhr.responseType = 'blob';
        xhr.onreadystatechange = function() {
            if (xhr.readyState === 4 && xhr.status === 200) {

                let blob = xhr.response;
                if (window.navigator.msSaveOrOpenBlob) { // IE浏览器下,SBIE
                    navigator.msSaveBlob(blob, fileName);
                } else {
                    var link = document.createElement("a");

                    //创建一个新的URL对象,blob是用来创建 URL 的 File 对象或者 Blob 对象​
                    link.href = window.URL.createObjectURL(blob);
                    link.download = fileName;
                    link.click();

                    //释放createObjectURL创建的URL对象
                    window.URL.revokeObjectURL(link.href);
                }

                resolve(xhr.response);

            }
        };
        xhr.send(formData);
    });
}

其实下载文件的时候传递参数的方式可以不使用FormData来创建对象传递,可以直接使用这种格式xhr.send('name=lihua&age=18');这里为了统一使用方式所以使用的FormData()来传递参数。

这里有个坑需要注意一下,我没有写xhr.setRequesHeader("Content-type","...")。因为浏览器会根据你的传递方式进行自动选择。如果我使用FormData传递数据,那么content-type会自动变为:

Content-Type:multipart/form-data; boundary=----WebKitFormBoundaryvz9jM571LgaBj0TA

而Content-Type:application/x-www-form-urlencoded; charset=UTF-8是默认的传递格式,但是如果你显示声明了这种传递方式,传递参数的时候就不再是正常看到的表单name:lihua,age:18这种对象格式。从而拿不到数据,这个时候需要将传递参数的方式转为xhr.send('name=lihua&age=18');而不是使用FormData来构造参数传递。

上面我统一用FormData()的原因是因为,这个代码改改,封装一下就可以应对所有的ajax使用状况,所以统一一下会减少今后的疑惑。

axios 下载

post:

download (params, url, name) {
  axios({
    url,
    method: 'post',
    headers: {
      'Content-Type': 'application/json;charset-UTF-8'
    },
    data: params,
    responseType: 'arraybuffer'
  }).then(res => {
    let headers = res.headers;
    let blob = new Blob([res.data], {
      type: headers['content-type']
    });
    let link = document.createElement('a');
    link.href = window.URL.createObjectURL(blob);
    if (!name) {
      const fileName = headers['content-disposition'];
      name = fileName.includes('filename') ? fileName.split('=')[1] : 'download'
    }
    link.download = name;
    let evt = document.createEvent('MouseEvent');
    evt.initEvent('click', true, true);
    link.dispatchEvent(evt);
  })
}

有些后端他写成了get方式,其实也是可以的

download (params, url, name) {
  axios({
    url, // url里面可以拼接参数
    method: 'get',
    headers: {
      'Content-Type': 'application/json;charset=UTF-8'
    },
    params,
    responseType: 'blob' //根据需要定
  }).then(res => {
    let headers = res.headers;
    let blob = new Blob([res.data], {
      type: headers['content-type']
    });
    let link = document.createElement('a');
    link.href = window.URL.createObjectURL(blob);
    if (!name) {
      const fileName = headers['content-disposition'];
      name = fileName.includes('filename') ? fileName.split('=')[1] : 'download'
    }   
    //如果name是乱码则需要先经过escape和decodeURIComponent处理
    name = decodeURIComponent(escape(name));
    link.download = name;
    let evt = document.createEvent('MouseEvent');
    evt.initEvent('click', true, true);
    link.dispatchEvent(evt);
  })
}

 

  • 0
    点赞
  • 0
    评论
  • 5
    收藏
  • 一键三连
    一键三连
  • 扫一扫,分享海报

©️2021 CSDN 皮肤主题: 技术黑板 设计师:CSDN官方博客 返回首页
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、C币套餐、付费专栏及课程。

余额充值