最近在开发一个文件上传功能,开发环境下,上传一个 100 多 M 的文件到服务端,需要花费接近 1 分钟。
前端代码是自己写的,也没有用第三方文件上传插件,普通的 jquery,大致长这样:
// 开始上传
var form_data = new FormData();
form_data.append('file', document.getElementById(id).files[0]);
$.ajax({
url: '/apps/upload',
type: 'POST',
data: form_data,
balabala...
});
后端看日志,其实处理得也没那么久,那时间耗在哪里了呢?
Completed 200 OK in 3786ms (Views: 0.2ms | ActiveRecord: 1131.2ms)
花了俩小时放狗搜,最后发现可能是 Rack 的问题:
Rack::Multipart::Parser.parse
中有一个循环,每次读取的 BUFSIZE 大小是 16384 个字节。所以文件很大的话,循环得就很多次了。
已经有人给 Rack 提了一个issue
解决方案也很简单,一位叫 darfux 的同学给出来了,写一个中间件把那个 BUFSIZE 变量覆写掉就好
class MultipartBufferSetter
def initialize(app)
@app = app
end
def call(env)
env.merge!(Rack::RACK_MULTIPART_BUFFER_SIZE => 100 * 1024 * 1024)
@app.call(env)
end
end
但是按照常理去理解,BUFSIZE 设置为 16384 应当是有原因的,现在被改得这么大有没有潜在的问题还需要观察一阵子,之前使用了 jquery-file-upload 这类第三方插件就没有遇到过这个问题。
今天早上查了下,jquery-file-upload 这个 js 插件有一个特性:
Chunked uploads: Large files can be uploaded in smaller chunks with browsers supporting the Blob API.