如何使用 python 请求上传文件?

Posted

技术标签:

【中文标题】如何使用 python 请求上传文件?【英文标题】:How to upload file with python requests? 【发布时间】:2014-04-29 07:47:48 【问题描述】:

我正在执行使用 Python 请求库上传文件的简单任务。我搜索了Stack Overflow,似乎没有人遇到同样的问题,即服务器没有收到文件:

import requests
url='http://nesssi.cacr.caltech.edu/cgi-bin/getmulticonedb_release2.cgi/post'
files='files': open('file.txt','rb')
values='upload_file' : 'file.txt' , 'DB':'photcat' , 'OUT':'csv' , 'SHORT':'short'
r=requests.post(url,files=files,data=values)

我用我的文件名填充“upload_file”关键字的值,因为如果我把它留空,它会说

Error - You must select a file to upload!

现在我明白了

File  file.txt  of size    bytes is  uploaded successfully!
Query service results:  There were 0 lines.

仅当文件为空时才会出现。所以我不知道如何成功发送我的文件。我知道该文件有效,因为如果我去这个网站并手动填写表格,它会返回一个很好的匹配对象列表,这就是我所追求的。我真的很感激所有的提示。

其他一些相关的线程(但没有回答我的问题):

Send file using POST from a Python script http://docs.python-requests.org/en/latest/user/quickstart/#response-content Uploading files using requests and send extra data http://docs.python-requests.org/en/latest/user/advanced/#body-content-workflow

【问题讨论】:

【参考方案1】:

如果upload_file 是文件,请使用:

files = 'upload_file': open('file.txt','rb')
values = 'DB': 'photcat', 'OUT': 'csv', 'SHORT': 'short'

r = requests.post(url, files=files, data=values)

requests 将发送一个多部分表单POST 正文,其中upload_file 字段设置为file.txt 文件的内容。

文件名将包含在特定字段的 mime 标头中:

>>> import requests
>>> open('file.txt', 'wb')  # create an empty demo file
<_io.BufferedWriter name='file.txt'>
>>> files = 'upload_file': open('file.txt', 'rb')
>>> print(requests.Request('POST', 'http://example.com', files=files).prepare().body.decode('ascii'))
--c226ce13d09842658ffbd31e0563c6bd
Content-Disposition: form-data; name="upload_file"; filename="file.txt"


--c226ce13d09842658ffbd31e0563c6bd--

注意filename="file.txt" 参数。

如果您需要更多控制,您可以为 files 映射值使用 2 到 4 个元素的元组。第一个元素是文件名,后跟内容,以及可选的内容类型标头值和可选的附加标头映射:

files = 'upload_file': ('foobar.txt', open('file.txt','rb'), 'text/x-spam')

这会设置替代文件名和内容类型,省略可选的标题。

如果您的意思是整个 POST 正文要从文件中获取(没有指定其他字段),那么不要使用 files 参数,只需将文件直接发布为 @ 987654332@。然后,您可能还想设置一个 Content-Type 标头,否则不会设置任何标头。见Python requests - POST data from a file。

【讨论】:

您好,如何发送多个同名文件?例如“附件”。 @William:您也可以使用 2 值元组序列,这样您就可以重复使用字段名称:files = [('attachment', open('attachment1.txt', 'rb')), ('attachment', open('attachment2.txt', 'rb'))]。每个元组是一对键和值。 您也可以使用files='file':('nameoffile',open('namoffile','rb'),'Content-Type':'text/html','other header'),'file2':('nameoffile2',open('nameoffile2','rb'),'Content-Type':'application/xml','other header'),但如果使用了files=,则不得使用headers='Content-Type':'blah blah'! -> @martijn-pieters:因为 multipart/form-data Content-Type 必须包含用于划分帖子正文中各部分的边界值。不设置 Content-Type 标头可确保请求将其设置为正确的值。 @MartijnPieters 这不会有泄露文件的风险吗? requests 关闭了吗? @MattMessersmith:不,它没有关闭。如果要关闭文件,请使用with open(...) as fobj:,并在files 映射中使用fobj【参考方案2】:

(2018) 新的 python 请求库简化了这个过程,我们可以使用 'files' 变量来表示我们要上传多部分编码的文件

url = 'http://httpbin.org/post'
files = 'file': open('report.xls', 'rb')

r = requests.post(url, files=files)
r.text

【讨论】:

请求库会自动关闭文件吗? 你好,我已经有一段时间没有使用这个库了。好问题。你能通过键入lsof 来帮助我和其他人吗? grep "filename" 并与我们分享您的结果?谢谢:) 使用lsof,文件似乎保持打开状态,或者至少,这是我解释以下结果的方式。在运行open 之前,lsof 表中没有关于filename 的记录。然后在执行open 之后,会出现多条具有read 访问权限的记录。执行requests.post后,记录还在,说明文件没有关闭。 (2021) 如果上传文件时还需要参数,可以添加params,如下:r = requests.post(url,files=files,params="key":value)【参考方案3】:

客户端上传

如果您想使用 Python requests 库上传单个文件,请请求 lib supports streaming uploads,它允许您发送大文件或流无需读入内存强>.

with open('massive-body', 'rb') as f:
    requests.post('http://some.url/streamed', data=f)

服务器端

然后将文件存储在server.py 端,以便将流保存到文件中而不加载到内存中。以下是使用Flask file uploads 的示例。

@app.route("/upload", methods=['POST'])
def upload_file():
    from werkzeug.datastructures import FileStorage
    FileStorage(request.stream).save(os.path.join(app.config['UPLOAD_FOLDER'], filename))
    return 'OK', 200

或使用werkzeug Form Data Parsing,如修复“large file uploads eating up memory”问题中所述,以避免在上传大文件时低效使用内存(约 60 秒内 st 22 GiB 文件。内存使用量恒定在 13 MiB 左右。)。

@app.route("/upload", methods=['POST'])
def upload_file():
    def custom_stream_factory(total_content_length, filename, content_type, content_length=None):
        import tempfile
        tmpfile = tempfile.NamedTemporaryFile('wb+', prefix='flaskapp', suffix='.nc')
        app.logger.info("start receiving file ... filename => " + str(tmpfile.name))
        return tmpfile

    import werkzeug, flask
    stream, form, files = werkzeug.formparser.parse_form_data(flask.request.environ, stream_factory=custom_stream_factory)
    for fil in files.values():
        app.logger.info(" ".join(["saved form name", fil.name, "submitted as", fil.filename, "to temporary file", fil.stream.name]))
        # Do whatever with stored file at `fil.stream.name`
    return 'OK', 200

【讨论】:

【参考方案4】:

@martijn-pieters 的答案是正确的,但是我想为data= 和另一边添加一些上下文,在您尝试上传文件的情况下,在 Flask 服务器中 JSON。

在请求方面,这如 Martijn 所述:

files = 'upload_file': open('file.txt','rb')
values = 'DB': 'photcat', 'OUT': 'csv', 'SHORT': 'short'

r = requests.post(url, files=files, data=values)

但是,在 Flask 端(此 POST 另一端的接收网络服务器),我不得不使用 form

@app.route("/sftp-upload", methods=["POST"])
def upload_file():
    if request.method == "POST":
        # the mimetype here isnt application/json
        # see here: https://***.com/questions/20001229/how-to-get-posted-json-in-flask
        body = request.form
        print(body)  # <- immutable dict

body = request.get_json() 不会返回任何内容。 body = request.get_data() 将返回一个包含很多内容的 blob,例如文件名等。

这是不好的部分:在客户端,将 data= 更改为 json= 会导致此服务器无法读取 KV 对!例如,这将导致上面的 正文:

r = requests.post(url, files=files, json=values). # No!

这很糟糕,因为服务器无法控制用户如何格式化请求;而json= 将成为请求用户的习惯。

【讨论】:

@martijn-pieters 只是看看你是否想在你的答案中包含任何这些,我会删除它;这对于从客户端和服务器的“双方”角度来回答这个问题的人可能很有用。【参考方案5】:

在 Ubuntu 中你可以这样应用,

将文件保存在某个位置(临时),然后打开并将其发送到 API

      path = default_storage.save('static/tmp/' + f1.name, ContentFile(f1.read()))
      path12 = os.path.join(os.getcwd(), "static/tmp/" + f1.name)
      data= #can be anything u want to pass along with File
      file1 = open(path12, 'rb')
      header = "Content-Disposition": "attachment; filename=" + f1.name, "Authorization": "JWT " + token
       res= requests.post(url,data,header)

【讨论】:

data变量的值是多少? 它可以是用户名之类的任何东西,我刚刚展示了如何将文件上传到 REST apis

以上是关于如何使用 python 请求上传文件?的主要内容,如果未能解决你的问题,请参考以下文章

requests请求接口---上传文件

python发送post请求上传文件,无法解析上传的文件

Python接口测试文件上传

Python 请求 - 上传 Zip 文件

Python接口测试,requests库的post请求进行文件上传

使用 python 请求将文件上传到 Nodejs