背景

我们有一个文件下载中心,提供一些软件、文档的下载。一直正常运行,但某天有同事反馈说他用手机下载某个软件的时候乱码了。

长这样


但我明明自己用手机试过,是能够正常下载的,是怎么回事呢?

问题排查

技术架构

这个文件服务其实很简单,直接使用 Nginx 作为文件服务器,利用 alias 指令映射请求的 URI 到文件系统的路径。类似这样:

location ^~ /download/software {
  alias "/";
  sendfile on;
  limit_rate      1024k;
  absolute_redirect off;
}

ps: aliasroot 都是用来做服务器路径映射的,只不过规则有差异。

思路

其实这个问题还是比较好查的,下载是浏览器行为,而且不同的浏览器表现有差异。

那么优先查浏览器就好了,那位同事使用的是 Firefox,而我小米手机自带的浏览器则是 Chrome 内核。

看起来就是 Firefox 不支持某些下载场景。

想到这里,我就有点好奇,浏览器的下载行为究竟是由谁来控制,它怎么知道要下载?

浏览器下载行为

下载都是一个个 HTTP请求,那想必和请求的响应头有关。

Content-Disposition

这个响应头我是很熟悉的,写下载文件的代码的时候,经常要给它指定文件名,例如

Content-Disposition: attachment; filename="example.pdf"

如果文件名是中文,通常还要做url编码

其实这个响应头的主要作用是控制浏览器预览还是直接下载文件
常见的值有两个:

  • inline:指示浏览器预览内容(如果浏览器支持),比如一些 pdf 文件
  • attachment:指示浏览器下载内容并提示用户保存文件,也即是我们常写的代码

Content-Type

这个响应头指定了相应内容的媒体类型(MIME 类型)。浏览器根据这个头信息来决定如何解析和显示响应内容。

我罗列了一些常见的Content-Type值:

  • 文本文件:text/html、text/plain、text/css、application/javascript
  • 图像文件:image/jpeg、image/png
  • 应用程序文件:application/json、application/octet-stream

回到问题

那么我的下载请求的响应头是什么?

居然是text/plain,居然是个文本!!!

(不得不说Chrome还是NB的,这都能下载)

接下来就是chapt时间:

Nginx 会使用mime.types文件来确定响应内容的Content-Type,这个文件里包含了常见的文件扩展名MIME类型的映射规则。

但我的文件里并没有 apk 文件的规则。

[root@localhost conf]# cat mime.types | grep apk
[root@localhost conf]#

而如果文件扩展名不在mime.types文件中,Nginx 将使用default_type指令来设置默认类型,于是我查了下我的配置:

default_type text/plain;

这下破案了。

如何解决

通过上面的文章,至少能知道两种方案:

  • 修改mime.types文件
  • 修改default_type

其实还有第三种方法,Nginx 提供了type指令来设置Content-Type

location ^~ /download/software {
  types {
    default_type application/octet-stream;
  } 
  alias "/";
  sendfile on;
  limit_rate      1024k;
  absolute_redirect off;
}

搞定收工。

本文由mdnice多平台发布


Jerry_ω_̥
1 声望0 粉丝