Warning: file_get_contents(/data/phpspider/zhask/data//catemap/8/python-3.x/16.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Python 如果“filename=”未指定,则带有多部分/表单数据的cgi.FieldStorage会尝试将二进制文件解码为UTF-8_Python_Python 3.x_File Upload_Cgi_Multipartform Data - Fatal编程技术网

Python 如果“filename=”未指定,则带有多部分/表单数据的cgi.FieldStorage会尝试将二进制文件解码为UTF-8

Python 如果“filename=”未指定,则带有多部分/表单数据的cgi.FieldStorage会尝试将二进制文件解码为UTF-8,python,python-3.x,file-upload,cgi,multipartform-data,Python,Python 3.x,File Upload,Cgi,Multipartform Data,当我使用cgi.FieldStorage解析多部分/表单数据请求或任何web框架(如使用cgi.FieldStorage的Pyramid)时,我很难处理从某些客户端上传的文件,这些客户端在部件的内容处理头中没有提供filename=file.ext 如果缺少filename=选项,FieldStorage将尝试将文件内容解码为UTF-8并返回字符串。很明显,许多文件是二进制文件,而不是UTF-8,因此会给出虚假的结果 例如: >>> import cgi >>>

当我使用cgi.FieldStorage解析多部分/表单数据请求或任何web框架(如使用cgi.FieldStorage的Pyramid)时,我很难处理从某些客户端上传的文件,这些客户端在部件的内容处理头中没有提供filename=file.ext

如果缺少filename=选项,FieldStorage将尝试将文件内容解码为UTF-8并返回字符串。很明显,许多文件是二进制文件,而不是UTF-8,因此会给出虚假的结果

例如:

>>> import cgi
>>> import io
>>> body = (b'--KQNTvuH-itP09uVKjjZiegh7\r\n' +
...         b'Content-Disposition: form-data; name=payload\r\n\r\n' +
...         b'\xff\xd8\xff\xe0\x00\x10JFIF')
>>> env = {
...     'REQUEST_METHOD': 'POST',
...     'CONTENT_TYPE': 'multipart/form-data; boundary=KQNTvuH-itP09uVKjjZiegh7',
...     'CONTENT_LENGTH': len(body),
... }
>>> fs = cgi.FieldStorage(fp=io.BytesIO(body), environ=env)
>>> (fs['payload'].filename, fs['payload'].file.read())
(None, '����\x00\x10JFIF')
浏览器和大多数HTTP库确实包含文件上传的filename=选项,但我目前正在处理一个不包含该选项的客户端,根据规范,省略文件名似乎是有效的

目前,我正在使用一种相当黑客的解决方法,将FieldStorage子类化,并将相关的内容处置头替换为具有以下文件名的头:

import cgi
import os

class FileFieldStorage(cgi.FieldStorage):
    """To use, subclass FileFieldStorage and override _file_fields with a tuple
    of the names of the file field(s). You can also override _file_name with
    the filename to add.
    """

    _file_fields = ()
    _file_name = 'file_name'

    def __init__(self, fp=None, headers=None, outerboundary=b'',
                 environ=os.environ, keep_blank_values=0, strict_parsing=0,
                 limit=None, encoding='utf-8', errors='replace'):

        if self._file_fields and headers and headers.get('content-disposition'):
            content_disposition = headers['content-disposition']
            key, pdict = cgi.parse_header(content_disposition)
            if (key == 'form-data' and pdict.get('name') in self._file_fields and
                    'filename' not in pdict):
                del headers['content-disposition']
                quoted_file_name = self._file_name.replace('"', '\\"')
                headers['content-disposition'] = '{}; filename="{}"'.format(
                        content_disposition, quoted_file_name)

        super().__init__(fp=fp, headers=headers, outerboundary=outerboundary,
                         environ=environ, keep_blank_values=keep_blank_values,
                         strict_parsing=strict_parsing, limit=limit,
                         encoding=encoding, errors=errors)
在我的第一次测试中使用body和env,现在可以工作了:

>>> class TestFieldStorage(FileFieldStorage):
...     _file_fields = ('payload',)
>>> fs = TestFieldStorage(fp=io.BytesIO(body), environ=env)
>>> (fs['payload'].filename, fs['payload'].file.read())
('file_name', b'\xff\xd8\xff\xe0\x00\x10JFIF')
有没有办法避免这种黑客行为,并告诉FieldStorage不要将其解码为UTF-8?如果您可以提供encoding=None或其他什么,那就太好了,但它看起来不支持这一点

我在处理从某些客户端上传的文件时遇到问题,这些客户端在部件的内容处理头中没有提供filename=file.ext

filename=参数实际上是服务器端确定部件表示文件上载的唯一方法。如果客户机省略了这个参数,它实际上并不是发送文件上传,而是发送一个纯文本表单字段。在这样一个字段中发送任意二进制数据在技术上仍然是合法的,但包括Python cgi在内的许多服务器环境都会被它弄糊涂

如果您能提供encoding=None或其他什么,那就太好了


如果将错误设置为“代理场景”,则至少可以从解码的字符中恢复原始字节。

我最终使用了一个更简单的FieldStorage子类来解决这个问题,因此我将其作为一个答案发布在这里。您不必重写_init_uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu

class MyFieldStorage(cgi.FieldStorage):
    @property
    def filename(self):
        if self._original_filename is not None:
            return self._original_filename
        elif self.name == 'payload':
            return 'file_name'
        else:
            return None

    @filename.setter
    def filename(self, value):
        self._original_filename = value
此外,正如@bobince的回答所指出的,您可以使用代理场景错误处理程序,然后将其编码回字节。这有点迂回,但也可能是最简单的解决方法:

>>> fs = cgi.FieldStorage(fp=io.BytesIO(body), environ=env, errors='surrogateescape')
>>> fs['payload'].file.read().encode('utf-8', 'surrogateescape')
b'\xff\xd8\xff\xe0\x00\x10JFIF'
设置errors=subrogateScape,然后执行string.encode'utf-8','subrogateScape'是一个很好的hack,谢谢!