Python:使用Cherrypy通过POST发送和接收大文件

Ben*_*vis 3 python upload post http cherrypy

我有一个小巧的Web服务器,需要能够通过http发布接收大文件。我目前有工作,但是一旦发送的文件太大(大约200mb),它就会失败。我正在使用curl发送测试帖子请求,当我尝试发送太大的文件时,curl吐出“随请求发送的实体超过了允许的最大字节数”。到处搜寻,这似乎是cherrypy的错误。

所以我猜要发送的文件需要分块发送吗?我用mmap尝试了一些东西,但是我做不到。处理文件上传的方法是否也需要能够接受大块数据?

saa*_*aaj 6

我以此DirectToDiskFileUpload为起点。为处理较大的上传而进行的更改是:

  1. server.max_request_body_size0(默认为100MB),
  2. server.socket_timeout60(默认10秒),
  3. response.timeout3600(默认300秒),
  4. 避免使用双重复制tempfile.NamedTemporaryFile

还采取了一些无用的措施来避免在内存中保存上载,从而禁用了标准CherryPy主体处理并cgi.FieldStorage改为手动使用。这是没有用的,因为有cherrypy._cpreqbody.Part.maxrambytes

阈值之后的字节阈值Part会将其数据存储在文件中,而不是字符串中。默认值为1000,就像cgiPython标准库中的模块一样。

我已经尝试了以下代码(由Python 2.7.4,CherryPy 3.6运行)和1.4GB文件。内存使用率(在gnome-system-monitor中)从未达到10MiB。根据实际写入磁盘的字节数,cat /proc/PID/iowrite_bytes几乎等于文件的大小。与标准cherrypy._cpreqbody.Partshutil.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)
Run Code Online (Sandbox Code Playgroud)