Closed matyhtf closed 2 years ago
当前版本中,HTTP 服务器在处理上传文件时,需要将所有内容保存在内存中。最大上传文件尺寸受限于 package_max_length 参数设置。基于此,底层无法支持超大文件上传,强行修改package_max_length 为一个巨大的值,会导致大量内存占用。
package_max_length
因此需要更换一个方式支持超大文件上传,在master进程中读取到文件上传请求,应当将文件内容直接写入磁盘,dispatch 时只传递文件名。
master
dispatch
$server->set([ 'upload_max_filesize' => 1 * 1024 * 1024 * 1024, // 最大允许上传 1G 'package_max_length' => 4 * 1024 * 1024, // 最大请求尺寸,所占内存的尺寸 ]);
默认 upload_max_filesize 为 0 ,表示不开启大文件上传,上传文件的 form-data 保存在内存中,受到 package_max_length 限制。 设置 upload_max_filesize 参数后,将开启大文件上传模块,客户端发送的 form-data 数据可以超过 package_max_length 限制,在master进程中会解析form-data,包含 filename 的 part 的内容直接写入磁盘文件,目录为 upload_tmp_dir 配置。
upload_max_filesize
0
form-data
filename
part
upload_tmp_dir
需要在 master 进程的 HTTP 解析部分进行处理,在检测到 Content-Type 为 multipart/form-data ,并且 upload_max_filesize 不为 0 并且 Content-Length 超过 package_max_length 时,进入超大文件上传模块。
HTTP
Content-Type
multipart/form-data
Content-Length
需要解析 multipart,不包含 filename 的部分仍然保存在内存中,包含 filename 的部分,则将内容直接写入临时文件,并修改属性值,追加文件上传信息。
multipart
-----------------------------7e196ceb17435e72200 Content-Disposition: form-data; name="file"; filename="test.jpg" Content-Type: text/plain $file_content ... 1G 数据 -----------------------------7e196ceb17435e72200
-----------------------------7e196ceb17435e72200 Content-Disposition: swoole-upload-file; name="testfile"; filename="test.jpg" Content-Type: text/plain tmp_file=$tmp_file; size=$file_size -----------------------------7e196ceb17435e72200
在 worker 进程中读取到 Content-Disposition: swoole-upload-file 后,将其转换为 Response::$files[testfile] 的值
worker
Content-Disposition: swoole-upload-file
Response::$files[testfile]
需要使用 async write file 异步写入文件,在线程池中完成文件的写操作,避免磁盘等待导致 reactor 线程阻塞。应使用pwrite,记录每次写入的 offset,保证数据一致性。
async write file
reactor
pwrite
offset
处理失败时,需要处理错误
若 offset 超过了 upload_max_filesize,则认为请求存在问题,返回 413 错误,进入错误处理流程
413
写磁盘失败时,返回 500 错误,进入错误处理流程
500
https://github.com/swoole/swoole-src/tree/rfc-1027
背景
当前版本中,HTTP 服务器在处理上传文件时,需要将所有内容保存在内存中。最大上传文件尺寸受限于
package_max_length
参数设置。基于此,底层无法支持超大文件上传,强行修改package_max_length
为一个巨大的值,会导致大量内存占用。因此需要更换一个方式支持超大文件上传,在
master
进程中读取到文件上传请求,应当将文件内容直接写入磁盘,dispatch
时只传递文件名。新增配置
默认
upload_max_filesize
为0
,表示不开启大文件上传,上传文件的form-data
保存在内存中,受到package_max_length
限制。 设置upload_max_filesize
参数后,将开启大文件上传模块,客户端发送的form-data
数据可以超过package_max_length
限制,在master
进程中会解析form-data
,包含filename
的part
的内容直接写入磁盘文件,目录为upload_tmp_dir
配置。实现细节
需要在
master
进程的HTTP
解析部分进行处理,在检测到Content-Type
为multipart/form-data
,并且upload_max_filesize
不为0
并且Content-Length
超过package_max_length
时,进入超大文件上传模块。需要解析
multipart
,不包含filename
的部分仍然保存在内存中,包含filename
的部分,则将内容直接写入临时文件,并修改属性值,追加文件上传信息。处理前:
处理后
在
worker
进程中读取到Content-Disposition: swoole-upload-file
后,将其转换为Response::$files[testfile]
的值写入方式
需要使用
async write file
异步写入文件,在线程池中完成文件的写操作,避免磁盘等待导致reactor
线程阻塞。应使用pwrite
,记录每次写入的offset
,保证数据一致性。错误处理
处理失败时,需要处理错误
超过限制
若
offset
超过了upload_max_filesize
,则认为请求存在问题,返回413
错误,进入错误处理流程写入失败
写磁盘失败时,返回
500
错误,进入错误处理流程