django 不支持 chunked-encoding 上传文件排查过程
# 背景
前端在上传文件时采用了分块编码头,然后在测试的过程中发现接口侧一直获取不到分块的数据。以下为此问题的排查过程。
技术架构:nodejs http request + django
- 前端发送的请求头如下
headers: {
'Cookie': '',
'Content-Type': 'application/octet-stream',
'Transfer-Encoding': 'chunked',
'Connection': 'keep-alive'
}
2
3
4
5
6
当通过 http 协议传输文件时,如果不知道文件类型时,就可以使用 application/octet-stream
请求头。
'Transfer-Encoding': 'chunked'
是 http 协议中的一种数据传输机制,允许 http 由客户端发送给服务器的数据可以分成多个部分,分块传输编码只在 http1.1 版本中提供。
在上传大文件时,如果不加上 'Transfer-Encoding': 'chunked'
头的话,相当于一次性把数据发送给服务器,服务器读取时将会占用大量内存,可能会导致内存溢出,导致程序出错。
- 在 flask 中,我们是可以直接通过
request.stream.read()
来读取前端传输的分块数据的,然而在 django 中并没有发现对应的属性或者函数,经过一番搜索,最终确定了由于 wsgi 规范的约束,导致 django 其实是不支持分块编码机制的
可以看到,在 wsgi 规范中,http 请求是必须包含 Content-Length
头的,而当使用 'Transfer-Encoding': 'chunked'
时,是会自动忽略 Content-Length
头的,因为每个分块的大小是不固定的。所以从规范上看,实现了 wsgi 规范的服务器都是不支持分块编码传输机制的。所以,这个问题只能暂时不分块传输了,改成了一次将文件二进制内容传给接口,然后写成服务器本地文件。
# 待跟进
经过阅读 django 的官方文档中的 wsgi 章节,发现了其实有好几种方式可以用于部署 django 应用,比如:Apache + mod_wsgi、gunicorn、uwsgi,但是后面两种方式都不支持 windows 系统,所以不考虑。看到有人说采用 Apache + mod_wsgi 部署 django 时,是支持分块编码传输机制的,有空的时候可以试试。现在服务运行是直接通过 runserver 的形式,其实也是不符合规范的,而且性能也较差,也需要优化。