惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

月光博客
月光博客
MyScale Blog
MyScale Blog
博客园 - Franky
The Cloudflare Blog
IT之家
IT之家
Blog — PlanetScale
Blog — PlanetScale
博客园 - 聂微东
WordPress大学
WordPress大学
Cyber Security Advisories - MS-ISAC
Cyber Security Advisories - MS-ISAC
T
The Blog of Author Tim Ferriss
让小产品的独立变现更简单 - ezindie.com
让小产品的独立变现更简单 - ezindie.com
罗磊的独立博客
Google DeepMind News
Google DeepMind News
P
Proofpoint News Feed
Martin Fowler
Martin Fowler
aimingoo的专栏
aimingoo的专栏
J
Java Code Geeks
腾讯CDC
雷峰网
雷峰网
Microsoft Azure Blog
Microsoft Azure Blog
G
Google Developers Blog
博客园 - 【当耐特】
美团技术团队
云风的 BLOG
云风的 BLOG

依云's Blog

Wayfire支持不缩放Xwayland啦 - 依云's Blog 使用wayvnc远程访问无头Wayfire会话 - 依云's Blog pacfiles: 高速的 pacman -F 替代品 用 Android 手机当电脑的话筒 - 依云's Blog 使用 ffmpeg 对音频文件进行响度归一化 - 依云's Blog 使用 nftables 屏蔽大量 IP - 依云's Blog YubiKey 初体验 - 依云's Blog fcitx5 码表同步方案 - 依云's Blog 我正在使用的火狐扩展(2024年版) - 依云's Blog 使用 PipeWire 实现自动应用均衡器 如果你发现你的 OOM Killer 在乱杀进程 使用 atuin 管理 shell 命令历史 btrfs 元数据满了怎么办 btrfs 翻车记 在 nspawn 里运行 docker Linux 上的字体配置与故障排除 新的 PaddleOCR 部署方案 使用 EasyEffects 调整 Bose 音箱的体验 让离线软件真正离线 我所讨厌的网页行为 tmux 状态栏优化 Google Chrome 中的字体设置 从 getmail6 到 offlineimap 微信消息通知的困扰 Qt 的字体渲染问题 Wayfire 迁移进展(四):不那么 high 的 DPI Wayfire 迁移进展(二):Xwayland HiDPI 以及 waybar Wayfire 迁移进展 不同情况下的图形效果 Wayland 初体验
Tornado 与文件上传
依云 · 2012-04-03 · via 依云's Blog

Tornado 自身是不支持大文件上传的。对于接收到的文件,它会把文件内容保存在内存里,而不是像 PHP 那样保存在临时文件里。这样对于大文件,问题很明显了——内容不够。所以,Tornado 上传文件的大小限制在 100M 以下了。Tornado 官方建议使用 nginx 的上传模块来处理文件上传。但是,我这个服务连 nginx 都没用的,不想为了这个还专门跑个 nginx。

于是,我尝试性地写了这么几百行代码。POST 上传的数据是multipart/form-data格式的,没有找到对应的 RFC,就对照着 HttpFox 显示的实际上传数据和 tornado 已有的代码进行修改。我理解的multipart/form-data格式是这样子的:

首先,在请求头里指定Content-Type: multipart/form-data; boundary=---------------------------12724806401896502337880080173,其中 boundary 的值是浏览器生成的,它用来分隔上传的不同文件。请求体一开始便是添加了--前缀的这个 boundary。刚开始我没太注意前边的横线多了两个,造成接收到的数据不对。在之后是\r\n,然后是和请求头格式一致的信息,如:

Content-Disposition: form-data; name="file"; filename="name.txt"
Content-Type: application/octet-stream

Content-Disposition中指明了文件对应表单的域名以及上传的文件名。文件名的编码看来没有定论,我的火狐用的是 UTF-8 编码。这些信息之后又是\r\n\r\n,然后是文件内容。还好这文件内容没有经过任何编码,直接保存即可。完了之后,如果还有下一个域的数据,那么在一个\r\n后就是类似的格式,否则在\r\n后是带--前缀和--后缀的 boundary。Tornado 的代码暗示数据结尾的\r\n是可选的。

整个格式是这样子的:

-----------------------------12724806401896502337880080173
Content-Disposition: form-data; name="file"; filename="name.txt""
Content-Type: application/octet-stream

This is file content.

-----------------------------12724806401896502337880080173
Content-Disposition: form-data; name="file"; filename="c"
Content-Type: text/plain

Another file content.
-----------------------------12724806401896502337880080173--

所以,要把数据保存到临时文件里去,不需要担心怎么进行流式解码了,只要确定了文件数据的起始和结束就好。为了做到这个,我只好每次都将读到的数据的最后一段长度为带前缀的 boundary 的长度加一的部分保存下来与下次读到的数据合并再处理,以此保存每段数据都是检查过 boundary 的。再加上一是为了防止\r\n被打断,下次找到 boundary 后取它前边的数据时出错。这个 edge case 还是今天写这文章时才想到,又花了不少时间测试。

最后记下 md5sum 的用法。计算 md5 时,把输出重定向到文件,校验时直接md5sum -c md5文件就可以了,不需要人工对比。

又,netcat 很好用。Arch 下使用 OpenBSD 版 netcat 发送 HTTP 请求的命令是:

nc.openbsd -q0 localhost 4322 < post

Ubuntu 现在默认的 netcat 就是 OpenBSD 版,所以直接用nc命令就可以了。