大部分文件下载都是在服务器放置好需要下载的资源,然后由服务器配置可以下载的文件类型,由系统去判断权限。如果文件比较小,不想通过系统去判断权限给出下载地址,如何在前端完成下载功能呢?

只需要将数据准备好,然后调用以下js函数即可:

调用方式:downLoadFile('test.txt', "Hello world!");

以上文档 转自:pakinguo(http://blog.csdn.net/dragon_q/article/details/46970699)

由于我的工作重心转向网盘的开发和维护,最近整了一个html版的文件浏览器demo,核心内容为上传和下载,积累了一点经验,这里把其中下载的内容拿出来谈一谈。(本文涉及的前端使用vue.js,后台使用node.js+express)

一个文件的下载

-下载链接

开始之前,回想起我们平时上网下载文件接触到的链接,都是譬如 
http://xxxxxx/name=cccc.rar 
http://xxxxxx/download/abcabc.exe 
等这种类型的链接。

  观察后不难发现这些都是get形式的链接。事实上如果用post,后台会在xhr的responseText中返回文件内容,无法保存到本地,不过可以直接append到页面上,然后让用户自己复制。综合以上考虑,因此我们需要用get建立传输的链接。

-从打开开始

  首先,我们根据需要的参数,拼出需要的get链接。要下载一个文件,需要传入它的文件地址和文件名,get链接就可以这么设计:

http://localhost:2333/downloadSingle?dir=f%3A%5Cdemo&name=desktop.ini
  • 1
  • 1

其中有两个参数:dir,name

  后台中根据这两个参数拿到对应的文件,用node.js里fs流的方式传出来:

var currFilePath = path.join(dir,name);
var fReadStream = fs.createReadStream(currFilePath);
fReadStream.on("data",(chunk) => res.write(chunk,"binary"));
  • 1
  • 2
  • 3
  • 1
  • 2
  • 3

  这样下载下来的文件,文件名是混乱的,应该在报文里传入Content-Disposition告诉浏览器文件名(Content-type可以不设置):

res.set({"Content-type":"application/octet-stream","Content-Disposition":"attachment;filename="+encodeURI(fileName)
});
  • 1
  • 2
  • 3
  • 4
  • 1
  • 2
  • 3
  • 4

  当然下载前应该判断下是否存在这个文件,最后后台的代码如下:

router.get('/downloadSingle',function(req, res, next){var currDir = path.normalize(req.query.dir),fileName = req.query.name,currFile = path.join(currDir,fileName),fReadStream;fs.exists(currFile,function(exist) {if(exist){res.set({"Content-type":"application/octet-stream","Content-Disposition":"attachment;filename="+encodeURI(fileName)});fReadStream = fs.createReadStream(currFile);fReadStream.on("data",(chunk) => res.write(chunk,"binary"));fReadStream.on("end",function () {res.end();});}else{res.set("Content-type","text/html");res.send("file not exist!");res.end();}});
});
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24

建立好后台之后,前端直接拿链接去测试。浏览器上打开(具体链接自己配置) 
http://localhost:2333/downloadSingle?dir=f%3A%5Cdemo&name=desktop.ini 
小文件在浏览上冒出来了,下载成功。

-无“闪现”下载

  你问怎么用代码下载下来?最简单的方法就是把这个链接放进window.open(url)里面,然后浏览器会新开个页面,建立链接后会关闭访问页,开始下载。然而,这种方法会闪一下,体验起来比较挫。接下来介绍一种没有”闪现”的方法。 
   
  我在《ie8下用iframe解决表单submit以及二级域名跨域的方法》中介绍过如何使用iframe来避免submit提交后页面的自动跳转(使用iframe屏蔽submit提交表单后的自动跳转),基本原理就是隐藏一个iframe,随浏览器怎么折腾它的死活,反正我们看不到就行了。 
  下面贴代码:

function downloadByIframe(url){var iframe = document.getElementById("myIframe");if(iframe){iframe.src = url;}else{iframe = document.createElement("iframe");iframe.style.display = "none";iframe.src = url;iframe.id = "myIframe";document.body.appendChild(iframe);}
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12

  需要下载文件时,调用downloadByIframe(url)即可。 
  不过这样的方法有个缺点,当后台找不到文件的时候,无法返回错误信息,不知道的还以为挂掉了。如果要即时相应的话,同样可以参考我这篇文章介绍的方法:使用onload获取后台返回在iframe中的数据

多个文件的下载

  以上介绍了下载一个文件时的处理方法。当然很可能会遇到多个文件要下载的情况。

  遇到多个文件要下载时,如果直接使用单个文件下载的方法,可以使用for循环,然后往里面扔downloadByIframe(url),简单暴力。 
  不过文件越多越容易引起强迫症,而且对于文件夹而言,无法使用这种方式下载。

  现在很多网盘对待多文件下载的情况时,会先把它们全部压缩起来,打包成一个文件再下载。通过这种思路,我们可以先在服务器端压缩文件,存到本地,然后通过这个压缩文件的路径和名称,拼出get的链接,再使用上文介绍的下载单个文件方法,即可顺利下载。使用这种思路,文件夹同样也可以打包进来。

-archiver压缩模块

  在开始之前,首先要介绍下在本文node.js中使用的压缩模块archiver,npm可以下载到。我是通过《 nodejs 中压缩/解压方案》(倪舒扬)这篇文章了解到archiver的,该篇文章比较详细的讨论了node.js其他几个压缩模块的利弊,有兴趣可以阅读一下。

  先贴代码:

var output = fs.createWriteStream(path.join("zip",zipName));
var archive = archiver.create('zip', {});
archive.pipe(output);   //和输出流相接
archive.append(fs.createReadStream(fileDir),{"name":fileName}); //塞文件进去
archive.finalize();
  • 1
  • 2
  • 3
  • 4
  • 5
  • 1
  • 2
  • 3
  • 4
  • 5

其中,archive.append只能放入单个文件,如果要放入文件夹或多个文件,需要使用archive.bulk()这个方法:

archive.bulk([ {cwd:currDir,    //设置相对路径src: [folderName1, fileName],//文件夹格式:xxx/**expand: currDir}
]);
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

官方文档对这个方法有更多的介绍archive.bulk传送门,有兴趣的可以啃一下。

archiver还提供了几个监听事件,用于处理阶段性的事件:

archive.on('error', function(err){//报错
});
archive.on('end', function(a){//压缩完毕生成文件
});
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

-代码设计

前端代码比较简单,直接贴出来(为省事我用的vue.js,大家可以用jQuery或者XMLHttpRequest自己写一个):

download:function(fileArray){var rootDir = this.$data.dir;this.$http.post("/download",{dir:rootDir,fileArray: fileArray}).then(function(result){//successvar data = result.data;if(data.code == "s_ok"){downloadByIframe(data.url);}else{alert(data.summary);}});
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15

后台综合上面的archiver,代码如下:

router.post('/download',function(req, res){var currDir = path.normalize(req.body.dir),fileArray = req.body.fileArray,fileNameArray = [];//将文件和文件夹分开命名fileArray.forEach(function(file) {if(file.type == 1){fileNameArray.push(file.name);}else{fileNameArray.push(path.join(file.name,"**"));  //文件夹格式:folderName/**}});if(fileArray.length == 0){res.send({"code":"fail", "summary":"no files"});return;}var output = fs.createWriteStream(path.join("zip",zipName));var archive = archiver.create('zip', {});archive.pipe(output);   //和输出流相接//打包文件archive.bulk([ {cwd:currDir,    //设置相对路径src: fileNameArray,expand: currDir}]);archive.on('error', function(err){res.send({"code":"failed", "summary":err});throw err;});archive.on('end', function(a){//输出下载链接var downloadUrl = "/downloadSingle?dir="+encodeURIComponent(zipDir)+"&name="+encodeURIComponent(zipName)+"&comefrom=archive";res.send({"code":"s_ok", "url":downloadUrl});});archive.finalize();
});
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42

其中需要强调的一点是,archiver的bulk中src传入的文件夹和文件写法不同,可以参考如下格式:

//[文件夹, 文件夹, 文件, 文件]
["calendar\**, nginx-1.8.1\**, .DS_Store, desktop.ini"]
  • 1
  • 2
  • 1
  • 2

锦上添花

-统一下载接口

  后台download这个接口经过改造,可以一起适配单个文件和多个文件下载的情况。 
  通过判断fileNameArray中是否为单个文件,可以建立一个分支:

if(filesCount == 1 && fileNameArray.length == 1){//只有一个文件的时候直接走getvar downloadUrl = "/downloadSingle?dir="+encodeURIComponent(currDir)+"&name="+encodeURIComponent(fileNameArray[0]);res.send({"code":"s_ok", "url":downloadUrl});
}else{//多个文件就压缩后再走get//用archiver压缩...
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

  这样前端不管用户选择了几个文件,只需要post download这个接口,拿到返回的链接塞进downloadByIframe()这个方法即可。

  至此,本文文件下载的方法全部介绍完毕。

以上node.js的文档转自:Lienviws(http://blog.csdn.net/liyijun4114/article/details/51743068#t1)

前端中关于下载文件问题相关推荐

  1. Blazor 中如何下载文件到浏览器

    Blazor 中如何下载文件到浏览器 目录 一.前言 二.方法一(导航跳转) 三.方法二(下载后传出) (一) 使用 RestSharp 下载 (二) 使用 BlazorDownloadFile 传出 ...

  2. android下载通知栏,Android开发中实现下载文件通知栏显示进度条

    android开发中实现下载文件通知栏显示进度条. 1.使用asynctask异步任务实现,调用publishprogress()方法刷新进度来实现(已优化) public class myasync ...

  3. 【踩坑】Linux java中ftp下载文件,解压文件损坏,以及图片下载打开只显示下载路径的问题

    [踩坑]Linux java中ftp下载文件,解压文件损坏,以及图片下载打开只显示下载路径的问题 一. 问题重现 二. 问题解决思路 1. 确认是不是上传就导致数据出错了 2. 是不是平台问题 三. ...

  4. 异步下载文件 java_在浏览器中异步下载文件监听下载进度

    在浏览器中异步下载文件,其实就是把服务器响应的文件先保存在内存中.然后再一次下载到磁盘.第二次下载过程,就是把内存的数据IO到磁盘,没有网络开销.速度极快. 之所以要先保存在内存,主要是可以在下载开始 ...

  5. js如何在浏览器中运行php文件下载,JavaScript_用JS在浏览器中创建下载文件,但受限于浏览器,很多情况下 - phpStudy...

    用JS在浏览器中创建下载文件 但受限于浏览器,很多情况下我们都只能给出个链接,让用户点击打开->另存为.如下面这个链接: file.js 用户点击这个链接的时候,浏览器会打开并显示链接指向的文件 ...

  6. 前端实现浏览器下载文件

    在前端页面,有时候需要通过链接下载文件,一般直接通过a链接就可以,href设置成文件的路径,但是又的文件,如PDF.JPG等浏览器支持的文件,通过a链接会直接打开.为此查找到下面的方法: 在html5 ...

  7. 前端接收并下载文件流 (blob对象)(word/.doc)

    1 根据文件路径下载文件模板,前端拿到的是stream 文件流 2 将文件流下载下来 第一步 当然是写请求方法了,如下 //下载模板 export function download (params) ...

  8. 前端批量打包下载文件

    最近有个需求需要前端批量打包文件的需求,然后经过搜索,使用到了 jszip 和 file-saver 这两个 js 插件. jszip 压缩插件,这个插件用起来个人觉得还算比较容易,官方文档下,仅仅需 ...

  9. android后台文件下载库,android中如何下载文件并显示下载进度

    最近开发中遇到需要下载文件的问题,对于一般的下载来说不用考虑断点续传,不用考虑多个线程,比如下载一个apk之类的,这篇文章讨论的就是这种情形. 这里主要讨论三种方式:AsyncTask.Service ...

最新文章

  1. 【剑指Offer学习】【全部面试题汇总】
  2. python获取渲染之后的网页_Phantomjs抓取渲染JS后的网页(Python代码)
  3. IE浏览器高级设置如何还原
  4. NodeJs本地搭建服务器,模拟接口请求,获取json数据
  5. 小程序-demo:快速开始
  6. Format - Numeric
  7. matlab中卡尔曼滤波,卡尔曼滤波算法及MATLAB实现
  8. DJL 教程 1.1 什么是AI人工智能
  9. 雷达原理之 多普勒效应原理及应用(一)
  10. 【ESP 保姆级教程】玩转emqx认证篇② ——认证安全之使用内置数据库(Mnesia)的密码认证
  11. 微信小程序例子——获取用户登录信息
  12. 推荐系统概述推荐系统算法简介
  13. inl和dnl matlab_matlab仿真inl dnl
  14. 中国智能制造系统架构(IMSA模型)
  15. 浅析BMS上电源芯片SBC应用
  16. (2.4)【服务型木马-winshell】最小木马:使用方法
  17. 运维知识体系V2.0-赵班长
  18. max模型怎么导入ue4_3DMax模型导入ue4的步骤是什么求详解?
  19. 【pytorch: can't optimize a non-leaf Tensor】
  20. 网络学习笔记:TCP/IP连网和Internet

热门文章

  1. hive 表创建及字段信息管理
  2. Python 实现MeanShift算法
  3. JavaScript - Ajax
  4. 解决字符终端下fbterm打不开yong输入法的问题
  5. cucumber Hooks @Before @After 不执行
  6. Django(1)——文件介绍
  7. Altium Designer 18 速成实战 第二部分 元件库(原理图库)创建 (一)元件符号的概述
  8. C语言中的随机数生成器
  9. Python三次样条插值
  10. MATLAB设置坐标轴颜色