私はDirectToDiskFileUpload
出発点として取りました。大きなアップロードを処理するために行う変更は次のとおりです。
server.max_request_body_size
に0
(デフォルト 100MB)、
server.socket_timeout
に60
(デフォルトは 10 秒)、
response.timeout
に3600
(デフォルトは 300 秒)、
- を使用して二重コピーを回避し
tempfile.NamedTemporaryFile
ます。
cgi.FieldStorage
また、アップロードをメモリに保持することを回避するために、標準の CherryPy 本体処理を無効にし、代わりに手動で使用するという無駄なアクションもいくつか行われています。あるからダメcherrypy._cpreqbody.Part.maxrambytes
。
Part
がそのデータを文字列ではなくファイルに保存するまでのバイト数のしきい値。cgi
Python の標準ライブラリのモジュールと同様に、デフォルトは 1000です。
次のコード (Python 2.7.4、CherryPy 3.6 で実行) と 1.4GB のファイルを試しました。メモリ使用量 ( gnome-system-monitor で) が 10MiB に達したことはありません。実際にディスクに書き込まれたバイト数によると、はほぼファイルのサイズですcat /proc/PID/io
。write_bytes
標準cherrypy._cpreqbody.Part
で、shutil.copyfileobj
明らかに2倍です。
#!/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)