相关文章推荐
会搭讪的蚂蚁  ·  C语言和C++有什么区别·  1 年前    · 
有爱心的花卷  ·  sequelize - 知乎·  1 年前    · 

#传统方法

利用iframe 或 form.submit 或 windows.open直接向后端发请求,后端返回文件流,后端处理成功后会直接返回到页面,浏览器会整理并打开自己的保存下载文件机制 。

1、利用form.submit直接向后端提交,后端返回文件流

1)前端代码:

 1 var downloadURL = "RestHandle.ashx";
 2 var testForm = $("<form>");   //定义一个form表单
 3 testForm.attr('style','');   //设置form表单属性
 4 testForm.attr('target','');
 5 testForm.attr('method','post');
 6 testForm.attr('action',downloadURL);
 8 var nameInput = $('<input>'); //构造formdata
 9 nameInput.attr('type','hidden'); 
10 nameInput.attr('name','fileName'); 
11 nameInput.attr('value','test.txt');  
13 $('body').append(testForm);  //将表单放置在web中
14 testForm.append(nameInput);   //将formdata添加到表单上
15 testForm.submit();   //表单提交
16 testForm.remove();  //表单移除

2)服务端代码,以asp.net为例:

 1 public class TestHandler : IHttpHandler
 4     public void ProcessRequest(HttpContext context)
 6         string fileName = context.Request["FileName"];//客户端传送过来的要下载的文件名
 7         string filePath = System.Web.HttpContext.Current.Server.MapPath("DownLoad/" + fileName);//路径
 9         FileInfo newFile = new FileInfo(filePath);
11         //以字符流的形式下载文件
12         FileStream fs = new FileStream(filePath, FileMode.Open);
13         byte[] bytes = new byte[(int)fs.Length];
14         fs.Read(bytes, 0, bytes.Length);
15         fs.Close();
16         context.Response.ContentType = "application/octet-stream";
17         //通知浏览器下载文件而不是打开
18         context.Response.AddHeader("Content-Disposition", "attachment;   filename=" + HttpUtility.UrlEncode(fileName, System.Text.Encoding.UTF8));
19         context.Response.BinaryWrite(bytes);
20         context.Response.Flush();
21         context.Response.End();
22     }
24     public bool IsReusable
25     {
26         get
27         {
28             return false;
29         }
30     }

  3)优缺点:

    优点 :兼容性良好,传统方式,不会出现URL长度限制问题;

    缺点:拿不到后端处理这个过程的时机,无法根据回调函数做交互以及进度提示

2、利于iframe直接向后端提交,后端返回文件流

  1)前端代码:

1 function downFileByIframe(parameters) {
2     var downloadURL = "TestHandler.ashx?FileName=test.txt";
3     var iframe = document.createElement("iframe");
4     iframe.src = downloadURL;
5     iframe.style.display = "none";
6     document.body.appendChild(iframe);

  2)后端代码:同上

  3)优缺点:

    优点: 兼容性较好

    缺点: *html中会增加多余的iframe元素,增加了维护成本;

           *拿不到后端处理这个过程的时机,无法根据回调函数做交互以及进度提示;

           *URL长度有限制;

3、使用windows.open下载文件

  1)前端代码

1 var downloadURL = "TestHandler.ashx?FileName=test.txt";
2 window.open(downloadURL);

  2)后端代码:同上

  3)优缺点:

    优点:  兼容性良好,代码简洁;

    缺点: *URL长度有限制;

           *拿不到后端处理这个过程的时机,无法根据回调函数做交互以及进度提示;

4、解决“无法根据回调函数做交互”的问题:ajax提交,后端返回在线文件地址

  利用ajax去提交请求,后端会返回一个线上的文件地址,前端可以通过原生的window.open打开这个地址就可以实现下载;

  也可以通过a标签设置href以及download属性,并自动点击实现其下载功能,关于其兼容性问题,可以判断download属性是否存在来弥补。

  1)优缺点:

  优点 :可以拿到其返回时机,可以做交互;

  缺点 :线上产生大量的中间临时文件,可以用设置时限来优化。解决方案:可使用大厂的云存储,从而减少临时文件的产生;

  2)前端代码:

 1 $.ajax({
 2     type: "post",
 3     url: "TestHandler.ashx",
 4     data: {'FileName':'test.txt'},
 5     success: function (res) {
 6         if (res.Status) {
 7             // window.open或者a标签下载 
 8             var isSupportDownload = 'download' in document.createElement('a');
 9             if (isSupportDownload) {
10                 var $a = $("<a>");
11                 $a.attr({
12                     href: res.url,
13                     download: 'filename'
14                 }).hide().appendTo($("body"))[0].click();
15             } else {
16                 window.open(res.url)
17             }
18         } else {
19             alert(res.Message);
20         }
21     }
22 })

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/

  支持场景 : 与上面的几种方案相比,这个模块提供的方案更加完善,而不是局限于某种方案,相当于将上面的几种方案结合了起来, 使用率很高。在源码中,我们可以看到在这个模块中针对各个浏览器和相应的属性是否支持进行了比较全面的兼容。其对应的下载文件方案包括了以下几种。

  • window.open(url)打开某个文件地址
  • iframe的框架中,设置src属性,通过iframe进行文件的下载,支持文件地址
  • 通过form标签,设置action的文件地址,然后通过form的提交来完成文件的下载
  •    1)前端代码:

     1 var downloadURL = "TestHandler.ashx";
     2 $.fileDownload(downloadURL, {
     3     httpMethod: 'post',
     4     data: { 'FileName': 'test.txt' },
     5     prepareCallback: function (url) {
     6         console.log("文件下载中...");
     7         // 数据加载动画
     8         $("body").append('<div id="Loading" style="background:url(images/load.png) top center no-repeat;"></div>');
     9     },
    10     abortCallback: function (url) {
    11         // 异常终止
    12         console.log("文件下载异常!!");
    13         $("#Loading").remove();
    14     },
    15     successCallback: function (url) {
    16         console.log("文件下载成功!!");
    17         $("#Loading").remove();
    18     },
    19     failCallback: function (html, url) {
    20         console.log("文件下载失败!!");
    21         $("#Loading").remove();
    22     }
    23 });

      2)后端代码:

     1 public void ProcessRequest(HttpContext context)
     3     string fileName = context.Request["FileName"];//客户端保存的文件名
     4     string filePath = System.Web.HttpContext.Current.Server.MapPath("DownLoad/" + fileName);//路径
     6     FileInfo newFile = new FileInfo(filePath);
     8     //以字符流的形式下载文件
     9     FileStream fs = new FileStream(filePath, FileMode.Open);
    10     byte[] bytes = new byte[(int)fs.Length];
    11     fs.Read(bytes, 0, bytes.Length);
    12     fs.Close();
    14     context.Response.ContentType = "application/octet-stream";
    15     context.Response.AddHeader("Content-Disposition", "attachment;   filename=" + HttpUtility.UrlEncode(fileName, System.Text.Encoding.UTF8));
    16     context.Response.BinaryWrite(bytes);
    18     //该cookie用于告诉jquery.fileDownload.js文件下载成功
    19     context.Response.Cookies.Add(new HttpCookie("fileDownload", "true"));
    20     context.Response.Flush();
    21     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)前端代码:

     1 function downByBlob_1(parameters) {
     2     var downloadURL = "TestHandler.ashx?FileName=zip.rar";
     3     let xhr = new XMLHttpRequest()
     4     let fileName = 'zip.rar' // 文件名称 
     5     xhr.open('GET', downloadURL, true);
     6     xhr.responseType = 'arraybuffer';
     7     //xhr.setRequestHeader('xx', 'xxxxx') // 请求头中添加信息
     8     xhr.onload = function () {
     9         if (this.status === 200) {
    10             let type = xhr.getResponseHeader('Content-Type')
    12             let blob = new Blob([this.response], { type: type })
    13             if (typeof window.navigator.msSaveBlob !== 'undefined') {
    14                 /*
    15                  * IE workaround for "HTML7007: One or more blob URLs were revoked by closing
    16                  * the blob for which they were created. These URLs will no longer resolve as 
    17                  * the data backing the URL has been freed." 
    18                  */
    19                 window.navigator.msSaveBlob(blob, fileName);
    20             } else {
    21                 let URL = window.URL || window.webkitURL;
    22                 let objectUrl = URL.createObjectURL(blob);
    23                 console.log(objectUrl);
    24                 //"blob:http://localhost:10614/3e48b856-fca6-4e4c-b780-1c4a7066f42e"
    25                 if (fileName) {
    26                     var a = document.createElement('a');
    27                     // safari doesn't support this yet
    28                     if (typeof a.download === 'undefined') {
    29                         window.location = objectUrl
    30                     } else {
    31                         a.href = objectUrl;
    32                         a.download = fileName;
    33                         document.body.appendChild(a);
    34                         a.click();
    35                         a.remove();
    36                     }
    37                 } else {
    38                     window.location = objectUrl;
    39                 }
    40             }
    41         }
    42     }
    43     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)前端代码:

     1 function downByBlob_2(parameters) {
     2     $.ajax({
     3         type: "post",
     4         url: "TestHandler.ashx",
     5         data: { 'FileName': 'zip.rar' },
     6         dataType: 'binary',
     7         responseType: 'arraybuffer',
     8         success: function (msg) {
     9             let blob = new Blob([msg]);
    11             console.log("Blob:" + msg); //msg 已不是乱码
    12             let url = window.URL.createObjectURL(blob); 
    14             let a = document.createElement("a");
    15             document.body.appendChild(a);
    16             a.href = url;
    17             a.download = 'zip.rar'; //命名下载名称
    18             a.click(); //点击触发下载  
    19             window.URL.revokeObjectURL(url); //下载完成进行释放
    20         }
    21     });
    

       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)前端代码:

    1  function downByFileSaver(parameters) {
    2     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://blog.csdn.net/wt346326775/article/details/83617663