【问题标题】:How to POST multiple FILES using Flask test client?如何使用 Flask 测试客户端发布多个文件?
【发布时间】:2013-12-28 06:37:54
【问题描述】:

为了测试 Flask 应用程序,我收到了一个 Flask 测试客户端 POSTing 请求,其中包含文件作为附件

def make_tst_client_service_call1(service_path, method, **kwargs):
    _content_type = kwargs.get('content-type','multipart/form-data')
    with app.test_client() as client:
        return client.open(service_path, method=method,
                           content_type=_content_type, buffered=True,               
                                             follow_redirects=True,**kwargs)

def _publish_a_model(model_name, pom_env):
    service_url = u'/publish/'
    scc.data['modelname'] = model_name
    scc.data['username'] = "BDD Script"
    scc.data['instance'] = "BDD Stub Simulation"
    scc.data['timestamp'] = datetime.now().strftime('%d-%m-%YT%H:%M')
    scc.data['file'] = (open(file_path, 'rb'),file_name)
    scc.response = make_tst_client_service_call1(service_url, method, data=scc.data)

处理上述 POST 请求的 Flask Server 端点代码是这样的

@app.route("/publish/", methods=['GET', 'POST'])
def publish():
    if request.method == 'POST':
        LOG.debug("Publish POST Service is called...")
        upload_files = request.files.getlist("file[]")
        print "Files :\n",request.files
        print "Upload Files:\n",upload_files
        return render_response_template()

我得到这个输出

Files:
ImmutableMultiDict([('file', <FileStorage: u'Single_XML.xml' ('application/xml')>)])

Upload Files:
[]

如果我改变了

scc.data['file'] = (open(file_path, 'rb'),file_name)

进入(认为它会处理多个文件)

scc.data['file'] = [(open(file_path, 'rb'),file_name),(open(file_path, 'rb'),file_name1)]

我仍然得到类似的输出:

Files:
ImmutableMultiDict([('file', <FileStorage: u'Single_XML.xml' ('application/xml')>), ('file', <FileStorage: u'Second_XML.xml' ('application/xml')>)])

Upload Files:
[]

问题: 为什么 request.files.getlist("file[]") 返回一个空列表? 如何使用烧瓶测试客户端发布多个文件,以便可以在烧瓶服务器端使用 request.files.getlist("file[]") 检索它?

注意:

  • 我想要一个烧瓶客户端,我不想要 curl 或任何其他基于客户端的解决方案。
  • 我不想在多个请求中发布单个文件

谢谢

已经引用了这些链接:

Flask and Werkzeug: Testing a post request with custom headers

Python - What type is flask.request.files.stream supposed to be?

【问题讨论】:

    标签: python post flask werkzeug


    【解决方案1】:

    您将文件作为名为@9​​87654321@ 的参数发送,因此您无法使用名称file[] 查找它们。如果你想获取所有名为file 的文件作为一个列表,你应该使用这个:

    upload_files = request.files.getlist("file")
    

    另一方面,如果你真的想从file[]阅读它们,那么你需要这样发送它们:

    scc.data['file[]'] = # ...
    

    file[] 语法来自 PHP,仅在客户端使用。当您将这样命名的参数发送到服务器时,您仍然使用 $_FILES['file'] 访问它们。)

    【讨论】:

    • 谢谢。是的。在浏览代码后,对于“getlist”,我昨晚意识到,getList 从 multiDict 返回给定键。由于对此缺乏了解,我使用了错误的键名。但是,您确实回答了我的问题,因此会接受您的问题。还发布了我在其他答案中收集的信息。
    【解决方案2】:

    Lukas 已经解决了这个问题,只是提供这些信息可能会对某人有所帮助

    Werkzeug 客户端通过在 MultiDict 中存储请求数据来做一些聪明的事情

    @native_itermethods(['keys', 'values', 'items', 'lists', 'listvalues'])
    class MultiDict(TypeConversionDict):
        """A :class:`MultiDict` is a dictionary subclass customized to deal with
        multiple values for the same key which is for example used by the parsing
        functions in the wrappers.  This is necessary because some HTML form
        elements pass multiple values for the same key.
    
        :class:`MultiDict` implements all standard dictionary methods.
        Internally, it saves all values for a key as a list, but the standard dict
        access methods will only return the first value for a key. If you want to
        gain access to the other values, too, you have to use the `list` methods as
        explained below.
    

    getList 调用在“请求”字典中查找给定的键。如果键不存在,则返回空列表。

    def getlist(self, key, type=None):
        """Return the list of items for a given key. If that key is not in the
        `MultiDict`, the return value will be an empty list.  Just as `get`
        `getlist` accepts a `type` parameter.  All items will be converted
        with the callable defined there.
    
        :param key: The key to be looked up.
        :param type: A callable that is used to cast the value in the
                     :class:`MultiDict`.  If a :exc:`ValueError` is raised
                     by this callable the value will be removed from the list.
        :return: a :class:`list` of all the values for the key.
        """
        try:
            rv = dict.__getitem__(self, key)
        except KeyError:
            return []
        if type is None:
            return list(rv)
        result = []
        for item in rv:
            try:
                result.append(type(item))
            except ValueError:
                pass
        return result
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-06-10
      • 1970-01-01
      • 2018-01-19
      • 1970-01-01
      • 2013-06-20
      • 1970-01-01
      相关资源
      最近更新 更多