我有一个cherrypy web 服务器,它需要能够通过http post 接收大文件。我目前有一些工作,但是一旦发送的文件变得太大(大约 200mb),它就会失败。我正在使用 curl 发送测试发布请求,当我尝试发送一个太大的文件时,curl 会吐出“与请求一起发送的实体超出了允许的最大字节数。” 环顾四周,这似乎是cherrypy的错误。
所以我猜测正在发送的文件需要分块发送?我用 mmap 尝试了一些东西,但我无法让它太有效。处理文件上传的方法是否也需要能够接受块数据?
我以此DirectToDiskFileUpload
为起点。它为处理大型上传所做的更改是:
server.max_request_body_size
到0
(默认 100MB),server.socket_timeout
到60
(默认 10 秒),response.timeout
到3600
(默认 300 秒),tempfile.NamedTemporaryFile
。还采取了一些无用的操作来避免将上传保存在内存中,这会禁用标准的 CherryPy 主体处理并cgi.FieldStorage
改为手动使用。它是无用的,因为有cherrypy._cpreqbody.Part.maxrambytes
。
在此之后的字节阈值
Part
会将其数据存储在文件而不是字符串中。默认为 1000,就像cgi
Python 标准库中的模块一样。
我已经尝试过以下代码(由 Python 2.7.4、CherryPy 3.6 运行)和 1.4GB 文件。内存使用量(在gnome-system-monitor中)从未达到 10MiB。根据实际写入磁盘的字节数,cat /proc/PID/io
'swrite_bytes
几乎是文件的大小。有了标准cherrypy._cpreqbody.Part
,shutil.copyfileobj
它显然是翻了一番。
#!/usr/bin/env python
# -*- coding: utf-8 -*-
import os
import tempfile
import cherrypy
config = {
'global' : {
'server.socket_host' : '127.0.0.1',
'server.socket_port' : 8080,
'server.thread_pool' : 8,
# remove any limit on the request body size; cherrypy's default is 100MB
'server.max_request_body_size' : 0,
# increase server socket timeout to 60s; cherrypy's defult is 10s
'server.socket_timeout' : 60
}
}
class NamedPart(cherrypy._cpreqbody.Part):
def make_file(self):
return tempfile.NamedTemporaryFile()
cherrypy._cpreqbody.Entity.part_class = NamedPart
class App:
@cherrypy.expose
def index(self):
return '''<!DOCTYPE html>
<html>
<body>
<form action='upload' method='post' enctype='multipart/form-data'>
File: <input type='file' name='videoFile'/> <br/>
<input type='submit' value='Upload'/>
</form>
</body>
</html>
'''
@cherrypy.config(**{'response.timeout': 3600}) # default is 300s
@cherrypy.expose()
def upload(self, videoFile):
assert isinstance(videoFile, cherrypy._cpreqbody.Part)
destination = os.path.join('/home/user/', videoFile.filename)
# Note that original link will be deleted by tempfile.NamedTemporaryFile
os.link(videoFile.file.name, destination)
# Double copy with standard ``cherrypy._cpreqbody.Part``
#import shutil
#with open(destination, 'wb') as f:
# shutil.copyfileobj(videoFile.file, f)
return 'Okay'
if __name__ == '__main__':
cherrypy.quickstart(App(), '/', config)
巨大的文件上传总是有问题的。如果在上传过程中连接关闭,您会怎么做?改用分块文件上传方法。