【问题标题】:cStringIO.StringO fails to save uploaded file stream when file is smaller than 1kByte当文件小于 1kByte 时,cStringIO.StringO 无法保存上传的文件流
【发布时间】:2015-10-27 03:04:33
【问题描述】:

我借用了这段代码来将文件流保存到磁盘,除了文件大小小于 1kb 时,它都可以工作。我收到此错误:

in stuff_uploaded:
copy(theFile.file.name, './tmp/'+theFile.filename) #saves temporary file to /cp2/tmp/

AttributeError: 'cStringIO.StringO' 对象没有属性 'name'

@cherrypy.expose
@cherrypy.tools.noBodyProcess()
def stuff_uploaded(self, theFile=None):
    import cgi
    import tempfile
    # convert the header keys to lower case
    lcHDRS = {key.lower():val for key, val in cherrypy.request.headers.iteritems()}
    class myFieldStorage(cgi.FieldStorage):
        """uses a named temporary file instead of the default non-named file; keeping it visibile (named), allows us to create a
        2nd link after the upload is done, thus avoiding the overhead of making a copy to the destination filename."""
        def make_file(self, binary=None):
            return tempfile.NamedTemporaryFile()
    formFields = myFieldStorage(fp=cherrypy.request.rfile,
                                headers=lcHDRS,
                                environ={'REQUEST_METHOD':'POST'},
                                keep_blank_values=True)
    theFile = formFields['theFile']
    # we now create a 2nd link to the file, using the submitted filename.
    from shutil import copy
    copy(theFile.file.name, './tmp/'+theFile.filename) #saves temporary file 
    msgs = csv_to_survey.match_fieldnames('./tmp/'+theFile.filename)
    return './tmp/'+theFile.filename

那么我能做些什么来确保 cStringIO.StringO 处理上传的小文件呢?

【问题讨论】:

  • 享受../../../etc/passwd 之类的文件名...

标签: python cherrypy cstringio


【解决方案1】:

直接打开写入文件即可:

with open('./tmp/'+theFile.filename, "w") as f:
    f.write(theFile.file.getvalue())

或者不管文件在磁盘上还是StringIO都处理它,把它当成类文件对象:

import shutil

with open('./tmp/'+theFile.filename, "w") as f:
    # If the file pointer might not be at the beginning of theFile.file, add:
    # theFile.file.seek(0)
    shutil.copyfileobj(theFile.file, f)
    # While:
    #     f.write(theFile.file.read())
    # would work most of the time, it involves holding the whole contents of the
    # file in memory at once (which you want to avoid; that's why CherryPy
    # uses temp files for larger data). shutil.copyfileobj does block by
    # block copies, which have fixed peak memory usage while still running
    # (almost) as fast

注意:这(以及您的原始解决方案)是不安全的,因为两次上传相同的文件将覆盖以前的文件,并且(取决于名称上的服务器过滤器)文件名可能会遍历文件系统以覆盖 tmp 之外的意外文件目录。

【讨论】:

  • 你的意思是不安全的意思是文件没有被保护不被覆盖,还是因为它可以被利用一些方式?如果我在文件名中添加一个随机字符串,可以解决两次覆盖文件的问题。
  • @MarcMaxson 使用随机的、唯一的文件名确实比使用用户输入更好,因为用户输入甚至可能包含斜杠!最好不要考虑用户提供的文件名,它不能被信任
  • 如果我只保留文件名中的 ascii 字母和空格怎么办?无论如何,这仅由管理员使用,并且在密码登录后使用,因此并不是真正不安全,但总是可以避免任何潜在的漏洞。
  • 我使用的是文件流方法 (cStringIO.StringO),因为它可以让我保存 50MB 或 100MB 的文件,而无需将它们两次放入内存。
  • @MarcMaxson 您可以读取和写入文件,例如 1MB。那么你的内存永远不会超过 1MB。是的,我想在文件名中保留字符白名单是安全的,但即使存在涉及编码的错误,所以要小心。
猜你喜欢
  • 2012-02-24
  • 2016-03-22
  • 1970-01-01
  • 2017-06-01
  • 2016-04-28
  • 1970-01-01
  • 2013-03-03
  • 2022-10-04
  • 2011-05-14
相关资源
最近更新 更多