#传统方法
利用 iframe 或 form.submit 或 Windows.open 直接向后端发请求, 后端返回文件流, 后端处理成功后会直接返回到页面, 浏览器会整理并打开自己的保存下载文件机制 .
1, 利用 form.submit 直接向后端提交, 后端返回文件流
1) 前端代码:
- var downloadURL = "RestHandle.ashx";
- var testForm = $("<form>"); // 定义一个 form 表单
- testForm.attr('style','display:none'); // 设置 form 表单属性
- testForm.attr('target','');
- testForm.attr('method','post');
- testForm.attr('action',downloadURL);
- var nameInput = $('<input>'); // 构造 formdata
- nameInput.attr('type','hidden');
- nameInput.attr('name','fileName');
- nameInput.attr('value','test.txt');
- $('body').append(testForm); // 将表单放置在 web 中
- testForm.append(nameInput); // 将 formdata 添加到表单上
- testForm.submit(); // 表单提交
- testForm.remove(); // 表单移除
2) 服务端代码, 以 ASP.NET 为例:
- public class TestHandler : IHttpHandler
- {
- public void ProcessRequest(HttpContext context)
- {
- string fileName = context.Request["FileName"];// 客户端传送过来的要下载的文件名
- string filePath = System.Web.HttpContext.Current.Server.MapPath("DownLoad/" + fileName);// 路径
- FileInfo newFile = new FileInfo(filePath);
- // 以字符流的形式下载文件
- FileStream fs = new FileStream(filePath, FileMode.Open);
- byte[] bytes = new byte[(int)fs.Length];
- fs.Read(bytes, 0, bytes.Length);
- fs.Close();
- context.Response.ContentType = "application/octet-stream";
- // 通知浏览器下载文件而不是打开
- context.Response.AddHeader("Content-Disposition", "attachment; filename=" + HttpUtility.UrlEncode(fileName, System.Text.Encoding.UTF8));
- context.Response.BinaryWrite(bytes);
- context.Response.Flush();
- context.Response.End();
- }
- public bool IsReusable
- {
- get
- {
- return false;
- }
- }
- }
3) 优缺点:
优点 : 兼容性良好, 传统方式, 不会出现 URL 长度限制问题;
缺点: 拿不到后端处理这个过程的时机, 无法根据回调函数做交互以及进度提示
2, 利于 iframe 直接向后端提交, 后端返回文件流
1) 前端代码:
- function downFileByIframe(parameters) {
- var downloadURL = "TestHandler.ashx?FileName=test.txt";
- var iframe = document.createElement("iframe");
- iframe.src = downloadURL;
- iframe.style.display = "none";
- document.body.appendChild(iframe);
- }
2) 后端代码: 同上
3) 优缺点:
优点: 兼容性较好
缺点: *html 中会增加多余的 iframe 元素, 增加了维护成本;
* 拿不到后端处理这个过程的时机, 无法根据回调函数做交互以及进度提示;
*URL 长度有限制;
3, 使用 Windows.open 下载文件
1) 前端代码
- var downloadURL = "TestHandler.ashx?FileName=test.txt";
- Windows.open(downloadURL);
2) 后端代码: 同上
3) 优缺点:
优点: 兼容性良好, 代码简洁;
缺点: *URL 长度有限制;
* 拿不到后端处理这个过程的时机, 无法根据回调函数做交互以及进度提示;
4, 解决 "无法根据回调函数做交互" 的问题: Ajax 提交, 后端返回在线文件地址
利用 Ajax 去提交请求, 后端会返回一个线上的文件地址, 前端可以通过原生的 Windows.open 打开这个地址就可以实现下载;
也可以通过 a 标签设置 href 以及 download 属性, 并自动点击实现其下载功能, 关于其兼容性问题, 可以判断 download 属性是否存在来弥补.
1) 优缺点:
优点 : 可以拿到其返回时机, 可以做交互;
缺点 : 线上产生大量的中间临时文件, 可以用设置时限来优化. 解决方案: 可使用大厂的云存储, 从而减少临时文件的产生;
2) 前端代码:
- $.Ajax({
- type: "post",
- url: "TestHandler.ashx",
- data: {'FileName':'test.txt'},
- success: function (res) {
- if (res.Status) {
- // Windows.open 或者 a 标签下载
- var isSupportDownload = 'download' in document.createElement('a');
- if (isSupportDownload) {
- var $a = $("<a>");
- $a.attr({
- href: res.url,
- download: 'filename'
- }).hide().appendTo($("body"))[0].click();
- } else {
- Windows.open(res.url)
- }
- } else {
- alert(res.Message);
- }
- }
- })
5, 解决 "无法根据回调函数做交互" 的问题: jQuery-download 插件
jQuery.download.JS 插件 GitHub 地址: https://github.com/johnculviner/jquery.fileDownload/blob/master/src/Scripts/jquery.fileDownload.js
jQuery.download.JS 插件 cdn 地址: https://www.bootcdn.cn/jquery.fileDownload/
支持场景 : 与上面的几种方案相比, 这个模块提供的方案更加完善, 而不是局限于某种方案, 相当于将上面的几种方案结合了起来, 使用率很高. 在源码中, 我们可以看到在这个模块中针对各个浏览器和相应的属性是否支持进行了比较全面的兼容. 其对应的下载文件方案包括了以下几种.
Windows.open(url) 打开某个文件地址
iframe 的框架中, 设置 src 属性, 通过 iframe 进行文件的下载, 支持文件地址
通过 form 标签, 设置 action 的文件地址, 然后通过 form 的提交来完成文件的下载
1) 前端代码:
- var downloadURL = "TestHandler.ashx";
- $.fileDownload(downloadURL, {
- httpMethod: 'post',
- data: { 'FileName': 'test.txt' },
- prepareCallback: function (url) {
- console.log("文件下载中...");
- // 数据加载动画
- $("body").append('<div id="Loading"style="background:url(images/load.png) top center no-repeat;"></div>');
- },
- abortCallback: function (url) {
- // 异常终止
- console.log("文件下载异常!!");
- $("#Loading").remove();
- },
- successCallback: function (url) {
- console.log("文件下载成功!!");
- $("#Loading").remove();
- },
- failCallback: function (HTML, url) {
- console.log("文件下载失败!!");
- $("#Loading").remove();
- }
- });
2) 后端代码:
- public void ProcessRequest(HttpContext context)
- {
- string fileName = context.Request["FileName"];// 客户端保存的文件名
- string filePath = System.Web.HttpContext.Current.Server.MapPath("DownLoad/" + fileName);// 路径
- FileInfo newFile = new FileInfo(filePath);
- // 以字符流的形式下载文件
- FileStream fs = new FileStream(filePath, FileMode.Open);
- byte[] bytes = new byte[(int)fs.Length];
- fs.Read(bytes, 0, bytes.Length);
- fs.Close();
- context.Response.ContentType = "application/octet-stream";
- context.Response.AddHeader("Content-Disposition", "attachment; filename=" + HttpUtility.UrlEncode(fileName, System.Text.Encoding.UTF8));
- context.Response.BinaryWrite(bytes);
- // 该 cookie 用于告诉 jQuery.fileDownload.JS 文件下载成功
- context.Response.Cookies.Add(new HttpCookie("fileDownload", "true"));
- context.Response.Flush();
- context.Response.End();
- }
注意: 这里的后端代码增加了一个名为 "fileDownload" 的 cookie 的返回; jQuery.download.JS 插件使用该 cookie 来判断是否下载成功, 从而进入成功回调函数 (successCallback);
# 新兴方案
1, 利用 Html5 的 download 属性进行下载
1) 前端代码
1 <a href="TestHandler.ashx?FileName=test.txt" download="test1.txt">DownloadAttrTest</a>
2) 后端代码: 同上
3) 优缺点:
优点: 代码简洁
缺点: 存在浏览器兼容性的问题
4) 参考:
https://www.zhangxinxu.com/wordpress/2016/04/know-about-html-download-attribute/
2, 利用 Html5 的 Blob 对象实现对文件流进行下载
(1), 使用原生 JS 发送 Ajax 实现
1) 前端代码:
- function downByBlob_1(parameters) {
- var downloadURL = "TestHandler.ashx?FileName=zip.rar";
- let xhr = new XMLHttpRequest()
- let fileName = 'zip.rar' // 文件名称
- xhr.open('GET', downloadURL, true);
- xhr.responseType = 'arraybuffer';
- //xhr.setRequestHeader('xx', 'xxxxx') // 请求头中添加信息
- xhr.onload = function () {
- if (this.status === 200) {
- let type = xhr.getResponseHeader('Content-Type')
- let blob = new Blob([this.response], { type: type })
- if (typeof Windows.navigator.msSaveBlob !== 'undefined') {
- /*
- * IE workaround for "HTML7007: One or more blob URLs were revoked by closing
- * the blob for which they were created. These URLs will no longer resolve as
- * the data backing the URL has been freed."
- */
- Windows.navigator.msSaveBlob(blob, fileName);
- } else {
- let URL = Windows.URL || Windows.webkitURL;
- let objectUrl = URL.createObjectURL(blob);
- console.log(objectUrl);
- //"blob:http://localhost:10614/3e48b856-fca6-4e4c-b780-1c4a7066f42e"
- if (fileName) {
- var a = document.createElement('a');
- // Safari doesn't support this yet
- if (typeof a.download === 'undefined') {
- Windows.location = objectUrl
- } else {
- a.href = objectUrl;
- a.download = fileName;
- document.body.appendChild(a);
- a.click();
- a.remove();
- }
- } else {
- Windows.location = objectUrl;
- }
- }
- }
- }
- xhr.send();
- }
2) 后端代码: 同上
(2), 使用结合 jq 发送 Ajax 请求实现, 需要引入 jQuery.binarytransport.JS 插件, 其扩展了 jq 的 Ajax 的 dataType 的设置;
jQuery.binarytransport.JS 插件 GitHub 地址: https://github.com/henrya/js-jquery/tree/master/BinaryTransport
注意: 当下载的是纯文本文件时, 是不需要引入插件, Ajax 也不用配置 dataType, 直接用 jq 的 Ajax 即可;
1) 前端代码:
- function downByBlob_2(parameters) {
- $.Ajax({
- type: "post",
- url: "TestHandler.ashx",
- data: { 'FileName': 'zip.rar' },
- dataType: 'binary',
- responseType: 'arraybuffer',
- success: function (msg) {
- let blob = new Blob([msg]);
- console.log("Blob:" + msg); //msg 已不是乱码
- let url = Windows.URL.createObjectURL(blob);
- let a = document.createElement("a");
- document.body.appendChild(a);
- a.href = url;
- a.download = 'zip.rar'; // 命名下载名称
- a.click(); // 点击触发下载
- Windows.URL.revokeObjectURL(url); // 下载完成进行释放
- }
- });
- }
2) 后端代码: 同上
3) 参考:
- http://www.henryalgus.com/reading-binary-files-using-jquery-ajax/
- https://blog.csdn.net/aydongzhiping/article/details/82462473
3, 新兴方案中的综合方案: file-saver
(1),FileSaver.JS 功能特点
FileSaver.JS 是一款基于 HTML5 完成文件保存的插件, 它可以帮我们直接从网页中导出多种格式文件.
同时对于那些本身不支持 HTML5 W3C saveAs() FileSaver 接口的浏览器, FileSaver.JS 也提供了支持.
使用 FileSaver.JS 可以让 Web 应用完美的生成文件, 或者保存那些不应该发送到外部服务器的敏感信息. 是一种简单易用的浏览器端文件保存方案.
(2), 安装
FileSaver.JS GitHub 地址: https://github.com/eligrey/FileSaver.js
可直接下载 FileSaver.JS 然后在页面中引用;
NPM,bower 安装:
- NPM install file-saver --save
- bower install file-saver
(3),demo: 使用 FileSaver.JS 下载后端返回的文件流;
1) 前端代码:
- function downByFileSaver(parameters) {
- saveAs('TestHandler.ashx?FileName=zip.rar');
- }
2) 后端代码: 同上
4, 新兴方案的优缺点:
优点: 技术新颖, 某些场景下使用方便;
缺点: 兼容性不好;
- # 参考
- https://juejin.im/post/5bd5547a6fb9a05cdd2d5109
- https://juejin.im/post/5bd1b0aa6fb9a05d2c43f004
- https://www.cnblogs.com/yunser/p/7629399.html
- ----------------------------------------------------
来源: https://www.cnblogs.com/willingtolove/p/10686208.html