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

    xiaoxiao2022-07-13  156

    遇到了一个上传文件和下载文件的业务,利用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使用状况,所以统一一下会减少今后的疑惑。

    最新回复(0)