WEB 中的文件下载(待修改、完善)
在 WEB 开发中,我们会期望用户在点击某个链接的时候,下载一个文件(不管这个文件能不能被浏览器解析,都要下载)。以前接触过一种方式,就是在响应 header 中设置 force-download :
Content-Type: application/force-download
Content-Disposition: attachment; filename="test.zip"
然而,这是一种 hack 方式,并不推荐使用:
Content-Type: application/force-download means "I, the web server, am going to lie to you (the browser) about what this file is so that you will not treat it as a PDF/Word Document/MP3/whatever and prompt the user to save the mysterious file to disk instead". It is a dirty hack that breaks horribly when the client doesn't do "save to disk".
引述自:Utility of HTTP header “Content-Type: application/force-download” for mobile?
有位小伙伴就遇到了不奏效的情况:
ATTENTION:
If you use any of the lines below your download will probably NOT WORK on Android 2.1.Content-Type: application/force-download
Content-Disposition: attachment; filename=MyFileName.ZIP
Content-Disposition: attachment; filename="MyFileName.zip"
Content-Disposition: attachment; filename="MyFileName.ZIP";
那么,究竟怎么办呢?接下来描述我的同事和我遇到的问题。
问题发现
最近接手了一个新项目,今天刚好有空熟悉一下之前的功能。于是打开线上地址,输入测试账号,进入一个列表页面,这个列表页面提供了下载数据为 Excel 文件的功能,点了一下下载链接,猛然发现,下载的文件名字怎么是 download ?为啥呢?
我用的浏览器是 Chrome 51 ,系统是 OS EI Capitan 10.11.5 。
我一同事 Chrome 47,可以完全正常下载!
先看看为啥我的浏览器不行吧!
第一步探索
打开 Chrome 开发者工具,查看 HTTP 请求,发现响应头部有如下两项:
Content-Type: application/octet-stream;charset=GBK
Content-Disposition: attachment; filename="%D6%D0%CE%C4.xlsx
噢,filename 那里多了一个双引号,去掉吧!
第二步探索
然而,引号去掉之后,问题依旧!什么情况?难道是 filename 需要引号包起来?
好吧,包起来试试!
第三步探索
包起来后问题依旧,什么鬼?
灵机一动,去看看别人怎么做的吧!于是找到别人网站一个下载 Excel 的页面,点击下载,发现响应 header 里面是这样的:
Content-Type: application/vnd.openxmlformats-officedocument.spreadsheetml.sheet;charset=UTF-8
Content-Disposition: inline;filename="%D6%D0%CE%C4.xlsx";filename*=utf-8''%D6%D0%CE%C4.xlsx
Content-Type 指明了具体的文件类型,然后 Content-Disposition 多了一个 filename*= ,这是什么东西? utf-8 是什么编码?
经过一堆胡乱搜索,猜测 utf-8 就是文件名的编码。为啥文件名要编码呢?呃,HTTP header 里面还未见过中文……
好了,我们后端的代码大致做法是这样的:
response.addHeader("Content-Type", "application/octet-stream");
response.addHeader("Content-Disposition", "attachment; filename=\"" + new String(fileName.getBytes("GBK"), "ISO-8859-1") + "\".xlsx");
看起来,只需要用 filename*= 附上编码就行了,于是后端代码改成:
response.addHeader("Content-Type", "application/octet-stream");
response.addHeader("Content-Disposition", "attachment; filename=\"" + new String(fileName.getBytes("GBK"), "ISO-8859-1") + "\".xlsx;filename*=GBK''" + new String(fileName.getBytes("GBK"), "ISO-8859-1"));
好了,我再点击下载,没问题!
第四步探索
看起来好像是 OK 了,但是,用 IE 试一下,又不正常了,文件名字不对了!
为什么呢?别人网站在 IE 下都能正常下载的!现在主要有两处区别:
我们的 Content-Type 没有写具体;
我们使用了 GBK 编码。
一思索,感觉编码的嫌疑较大,为啥呢?因为对于文件下载,浏览器根本不用管文件内容是个啥,只需要按照二进制流写入本地磁盘就好了,并且,此处也只是文件名错了,下载下来的文件内容还是没问题的。
那就改编码吧,改成 UTF-8 :
response.addHeader("Content-Type", "application/octet-stream");
response.addHeader("Content-Disposition", "attachment; filename=\"" + new String(fileName.getBytes("UTF-8"), "ISO-8859-1") + "\".xlsx;filename*=UTF-8''" + new String(fileName.getBytes("UTF-8"), "ISO-8859-1"));
经测试,一切正常!
总结
在文件下载功能中,一般都会借助于这两个 header 来达到效果,那么两个 header 的具体作用是什么呢?
Content-Type:告诉浏览器当前的响应体是个什么类型的数据。当其为 application/octet-stream 的时候,就说明 body 里面是一堆不知道是啥的二进制数据。
Content-Disposition:用于向浏览器提供一些关于如何处理响应内容的额外的信息,同时也可以附带一些其它数据,比如在保存响应体到本地的时候应该使用什么样的文件名。
细想一下, Content-Type 好像对于文件下载没什么作用?事实上的确如此。可是再想一下,如果浏览器不理会 Content-Disposition ,不下载文件怎么办?如果此时提供了 Content-Type ,至少浏览器还有机会根据具体的 Content-Type 对响应体进行处理。
可是为什么浏览器会不理会 Content-Disposition 呢?因为这个 Content-Disposition 头部并不是 HTTP 标准中的内容,只是被浏览器广泛实现的一个 header 而已。
话题转一转, Content-Disposition 的语法见此处,其中相对重要的点此处罗列一下:
常用的 disponsition-type 有
inline和attachment:inline:建议浏览器使用默认的行为处理响应体。
attachment:建议浏览器将响应体保存到本地,而不是正常处理响应体。
Content-Disposition 中可以传入 filename 参数,有两种形式:
filename=yourfilename.suffix:直接指明文件名和后缀。
filename*=utf-8''yourfilename.suffix:指定了文件名编码。其中,编码后面那对单引号中还可以填入内容,此处不赘述,可参考规范。
有些浏览器不认识
filename*=utf-8''yourfilename.suffix(估计因为这东西比较复杂),所以最好带上filename=yourfilename.suffix。
WEB 中的文件下载(待修改、完善)的更多相关文章
- Eclipse中把Java工程修改成web工程
Eclipse中把Java工程修改成web工程 点击项目:右击:选择properties--输入project facets,将“Dynamic Web Module”打勾即可:
- Entity Framework 6 Recipes 2nd Edition(9-4)译->Web API 的客户端实现修改跟踪
9-4. Web API 的客户端实现修改跟踪 问题 我们想通过客户端更新实体类,调用基于REST的Web API 服务实现把一个对象图的插入.删除和修改等数据库操作.此外, 我们想通过EF6的Cod ...
- 优化Web中的性能
优化Web中的性能 简介 web的优化就是一场阻止http请求最终访问到数据库的战争. 优化的方式就是加缓存,在各个节点加缓存. web请求的流程及节点 熟悉流程及节点,才能定位性能的问题.而且优化的 ...
- 在web中使用windows控件,实现摄像头功能
最近做的一个Web版的视频会议项目,需要在网页中播放来自远程摄像头采集的实时视频,我们已经有了播放远程实时视频的使用C#编写的windows控件,如何将其嵌入到网页中去了?这需要使用一种古老的技术,A ...
- 在Web中使用Windows控件
版权声明:本文为博主原创文章,未经博主允许不得转载. 目录(?)[+] 将Net控件转化为ActiveX控件 1GUID 2实现IObjectSafety接口 3程序集设定 制作安装程序 Web集 ...
- HT for Web中3D流动效果的实现与应用
流动效果在3D领域有着广泛的应用场景,如上图中医学领域可通过3D的流动直观的观察人体血液的流动,燃气领域可用于监控管道内流动的液体或气体的流向.流速和温度等指标. 如今企业数据中心机房普遍面临着设备散 ...
- Web中的图标
随着时代的变迁与技术的不断的更新,在当今这个时代,Web中的图标(Icons)不再仅仅是局限于<img>.除了<img>直接调用Icons文件之外,还有Sprites(俗称雪碧 ...
- 在Web中如何使用Windows控件(ActiveX)[转]
最近做的一个Web项目,需要在网页中播放摄像头采集的实时视频,我们已经有了播放视频的使用C#编写的windows控件,如何将其嵌入到网页中去了?这需要使用一种古老的技术,ActiveX. 1.将.Ne ...
- 在Web中实现C/S模式的Tab
在探讨C/S模式的Tab之前,我们先总结一下B/S模式的Tab通常是什么样的.web中常见的tab设计通常是用于分节展示大量信息以提高页面空间的利用率,而且这些信息通常是静态的,或者交互比较简单.通过 ...
随机推荐
- 机器学习--支持向量机 (SVM)算法的原理及优缺点
一.支持向量机 (SVM)算法的原理 支持向量机(Support Vector Machine,常简称为SVM)是一种监督式学习的方法,可广泛地应用于统计分类以及回归分析.它是将向量映射到一个更高维的 ...
- 第二章 简单的HTTP协议
第二章 简单的HTTP协议 针对HTTP协议结构进行讲解 1.通过请求和响应的交换来达成通信目的 应用HTTP协议时,必定是一端担任客户端角色,另一端担任服务器端角色. [请求报文]是由请求方法.UR ...
- go 创建自己的区块
package main import ( "time" "crypto/sha256" "bytes" ) //区块体 type Bloc ...
- SQL查询--关于查询的练习题
下面的练习题出自LeetCode:https://leetcode-cn.com/problemset/database/,有兴趣的可以去上面刷刷题 练习题1:超过经理收入的员工 分析: 使用sql ...
- 实现迭代器(__next__和__iter__)
目录 一.简单示例 二.StopIteration异常版 三.模拟range 四.斐波那契数列 一.简单示例 死循环 class Foo: def __init__(self, x): self.x ...
- 07-selenium、PhantomJS(无头浏览器)
selenium(自动化测试工具可用于在爬虫中解决js动态加载问题) 简介(本质就是模仿浏览器工作) Selenium 是什么?一句话,自动化测试工具.它支持各种浏览器,包括 Chrome,Safar ...
- Appium+java ---- Intellij IDEA +genymotion安装配置
引用文章:https://www.cnblogs.com/kaola8023/p/8442686.html Intellij IDEA 中配置Android SDK File-Project Stru ...
- MySQL(8)---游标
Mysql(8)-游标 上一遍博客写了有关存储过程的语法知识 Mysql(7)---存储过程 游标或许你在工作中很少用到,但用不到不代表不去了解它,但你真正需要它来解决问题的时候,再花时间去学习很可能 ...
- 运行时报:尝试加载 Oracle 客户端库时引发 BadImageFormatException,如果在安装 32 位 Oracle 客户端组件的情况下以 64 位模式运行,将出现此问题
运行环境为: Windows Server2012 Oracle11g 32位数据库+客户端 IIS发布后提示错误信息: “尝试加载 Oracle 客户端库时引发 BadImageFormatExc ...
- 采用邻接矩阵表示图的深度优先搜索遍历(与深度优先搜索遍历连通图的递归算法仅仅是DFS的遍历方式变了)
//采用邻接矩阵表示图的深度优先搜索遍历(与深度优先搜索遍历连通图的递归算法仅仅是DFS的遍历方式变了) #include <iostream> using namespace std; ...