WEB下载文件的各种实现及错误处理

通常我们使用AJAX向服务器请求数据,但是这些数据都是文本,而对于请求文件(二进制流),我们需要花点功夫进行处理。

本文从传统的下载实现方案到全新的HTML5 Blob下载方案进行比较全面的介绍。

一、 标签发起下载

一般情况下,定义一个标签,href指向一个浏览器无法处理的文件就会发起下载。既然文件下载如此简单,那为什么还要写文章讨论它呢?
它只能只能使用GET 请求,直接使用的话,会出现页面跳转或弹窗,甚至可能被浏览器拦截。

二、
表单发起下载

提交表单,若action指向的是文件,亦可以发起下载,定义属性method="POST"即可发起POST请求,显然是比上面更优的方案。同样会出现页面跳转或弹窗。这里讨论的是以一种异步的方式发起下载,在不主张简单问题复杂化的情况下,第三点(重点)对这两种简单的方式进行特殊处理。

三、 利用').appendTo(document.body); // 给iframe传递一个错误处理回调函数等遇到错误时调用 $iframe[0].errorCallback = opt.onError; if (opt.method.toUpperCase() === "POST") { // POST 方式 var iframeDoc = $iframe.prop("contentDocument") || $iframe.prop("contentWindow").document, // 获取iframe的document对象 $form = null, data = opt.data; // 为iframe写入一个form元素,利用该form元素发起文件下载请求 iframeDoc.write(''); $form = $(iframeDoc).find("form"); // 获取该form元素 // 带请求参数的情况 if (data instanceof Object) { if (Array.isArray(data)) { // data是数组形式 data.forEach(function (o) { if (o.value) $("").prop(o).appendTo($form); }); } else { // data是对象形式 for (var n in data) { $("").prop({ name: n, value: data[n] }).appendTo($form); } } } // 提交表单 $form.submit(); } else { // 默认 GET 方式 url.indexOf("?") < 0 ? url += "?" : url += "&"; window.open(url + $.param(opt.data), iframeName); } // 移除临时iframe setTimeout(function () { $iframe.remove(); }, opt.timeout); return this; }

这里遇到文件下载的一个经典问题:错误处理。你有没有发现,很多网站在我们点击下载文件时,会弹出类似提示:”如果浏览器没有发起下载请再点击……“,这是因为通过表单的方式发起下载我们并没有相关接口能够获得请求状态。不过我们并非完全措手无策,上面方法,在遇到服务器主动报错请求时可以得到错误反馈,例如服务器找不到文件或者业务逻辑不符合(非程序错误)拒绝了请求,通过补充下面函数进行处理:

/**
* @function download.error
* @param {Object} [frameWindow] iframe 的 window 对象
* @param {String} [text] 错误提示文本
*/
download.error = function (frameWindow, text) {
    var frameElement = frameWindow.frameElement;
    text = frameElement.errorCallback(text);
    document.body.removeChild(frameElement);
    text && alert(text);
}

此时,有点类似JSONP请求,服务器必须返回指定HTML字符串,并且指定HTML响应头Content-Type: text/html,例如配合上面的代码可以这样返回:



  下载失败
  

这样我们已经拥有了一个类似发起$.ajax这样的方法发起下载请求。值得注意的是,如果文件是浏览器能够识别的,很有可能是被浏览器打开而不是下载,例如txtjpg等文件,对于用于下载的文件,服务器应该明确指定一个下载请求头:Content-Type: application/force-download
那么问题来了,为什么我们不直接使用$.ajax这种普通AJAX请求来实现文件下载而非得这般折腾?如果你对兼容性要求不高,下面介绍一个更加优雅的方式。

四、 HTML5 XHR2与Blob接口实现文件下载

直接使用类似$.ajax等 AJAX 下载文件是不行的。