收藏本站腾讯微博新浪微博

经典论坛

 找回密码
 注册

QQ登录

只需一步,快速开始

蓝色理想 最新研发动态 网站开通淘帖功能 - 蓝色理想插件 论坛内容导读一页看论坛 - 给官方提建议

论坛活动及任务 地图和邮件任务 请多用悬赏提问 热夏来袭,选一款蓝色理想的个性T恤吧!

手机上论坛,使用APP获得更好体验 急需前端攻城狮,获得内部推荐机会 论坛开通淘帖功能,收藏终于可以分类了!

搜索
查看: 1535|回复: 0

[教程] Web开发中的文件下载问题

[复制链接]
发表于 2016-7-4 15:59:33 | 显示全部楼层 |阅读模式
在 WEB 开发中,我们会期望用户在点击某个链接的时候,下载一个文件(不管这个文件能不能被浏览器解析,都要下载)。深圳达内web前端培训(sz.web.tedu.cn)专家将为大家解决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”.

    QuentinUtility 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”;

    Jörg WagnerAndroid and the HTTP download file headers

那么,究竟怎么办呢?接下来描述我的同事和我遇到的问题。

问题发现

最近接手了一个新项目,今天刚好有空熟悉一下之前的功能。于是打开线上地址,输入测试账号,进入一个列表页面,这个列表页面提供了下载数据为 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

经过一堆胡乱搜索,猜测 utf-8 就是文件名的编码。为啥文件名要编码呢?呃,HTTP header 里面还未见过中文……Content-Type 指明了具体的文件类型,然后 Content-Disposition 多了一个 filename*= ,这是什么东西? utf-8 是什么编码?

好了,我们后端的代码大致做法是这样的:

    response.addHeader("Content-Type","application/octet-stream");

    response.addHeader("Content-Disposition","attachment; filename=\""+newString(fileName.getBytes("GBK"),"ISO-8859-1")+"\".xlsx");

看起来,只需要用 filename*= 附上编码就行了,于是后端代码改成:

    response.addHeader("Content-Type","application/octet-stream");

    response.addHeader("Content-Disposition","attachment; filename=\""+newString(fileName.getBytes("GBK"),"ISO-8859-1")+"\".xlsx;filename*=GBK''"+newString(fileName.getBytes("GBK"),"ISO-8859-1"));

好了,我再点击下载,没问题!看起来,只需要用 filename*= 附上编码就行了,于是后端代码改成:

第四步探索

看起来好像是 OK 了,但是,用 IE 试一下,又不正常了,文件名字不对了!

为什么呢?别人网站在 IE 下都能正常下载的!现在主要有两处区别:

    我们的 Content-Type 没有写具体;

    我们使用了 GBK 编码。

一思索,感觉编码的嫌疑较大,为啥呢?因为对于文件下载,浏览器根本不用管文件内容是个啥,只需要按照二进制流写入本地磁盘就好了,并且,此处也只是文件名错了,下载下来的文件内容还是没问题的。

那就改编码吧,改成 UTF-8 :

    response.addHeader("Content-Type","application/octet-stream");

    response.addHeader("Content-Disposition","attachment; filename=\""+newString(fileName.getBytes("UTF-8"),"ISO-8859-1")+"\".xlsx;filename*=UTF-8''"+newString(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培训, 免费试听,或周末免费参加深圳web培训公开课程试听,这里有强大的师资阵容,“师者,所以传道授业解惑也”,讲师,是教育的主体,是影响整体办学质量、 学员学习质量的重要因素。所以深圳达内 web培训竭尽全力为学员提供最强大的师资力量,让学员接受最好的教育,学习最前沿的技术。掌握一门牛逼闪闪技术,待遇自然不在话下。 选择培训机构等于选择未来开发行业的根基,优秀的web培训机构不仅仅是传授专业开发知识,更重要的是培养学员的开发思维和动手能力,可谓授人以鱼不如授人以渔!而这些,达内可以做到!
您需要登录后才可以回帖 登录 | 注册

本版积分规则

QQ|小黑屋|Archiver|手机版|blueidea.com ( ICP05002321 )  

GMT+8, 2020-1-20 03:25 , Processed in 0.093515 second(s), 8 queries , Gzip On, Memcache On.

Powered by Discuz! X3.2 Licensed

© 2001-2013 Comsenz Inc.

快速回复 返回顶部 返回列表