python请求文件上传

时间:2014-03-21 18:57:14

标签: python file-upload python-requests

我正在执行使用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.

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

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

5 个答案:

答案 0 :(得分:141)

如果要将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参数,只需将文件直接发布为{{1} }。然后,您可能还想设置data标题,否则将不会设置。

答案 1 :(得分:17)

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

    func tableView(_ tableView: UITableView, trailingSwipeActionsConfigurationForRowAt indexPath: IndexPath) -> UISwipeActionsConfiguration? {
    let delete = UIContextualAction(style: .normal, title: "") { (action, view, completion) in
        print("Delete Pressed")
        completion(true)
    }

    delete.backgroundColor = UIColor.clear
    delete.image = #imageLiteral(resourceName: "trash")

    let config = UISwipeActionsConfiguration(actions: [delete])
    config.performsFirstActionWithFullSwipe = false

    return config
}

func tableView(_ tableView: UITableView, willBeginEditingRowAt indexPath: IndexPath) {
    tableView.subviews.forEach { subview in
        if (String(describing: type(of: subview)) == "UISwipeActionPullView") {
            if (String(describing: type(of: subview.subviews[0])) == "UISwipeActionStandardButton") {
                if let actionButtonCGRect = actionButtonCGRect {
                    subview.subviews[0].bounds = actionButtonCGRect
                } else {
                    actionButtonCGRect = subview.subviews[0].frame.insetBy(dx: 0, dy: 10)
                    subview.subviews[0].bounds = actionButtonCGRect!
                }

                subview.subviews[0].layer.cornerRadius = 10
                subview.subviews[0].clipsToBounds = true
                subview.subviews[0].backgroundColor = UIColor.red
            }
        }
    }
}

答案 2 :(得分:8)

客户赞扬

如果您想使用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,以避免在上传大文件时低效使用内存(第22个GiB文件大约60秒。内存使用率恒定为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

答案 3 :(得分:0)

@martijn-pieters 答案是正确的,但是我想在 Flask 服务器中向 data= 以及另一端添加一些上下文,以防您尝试上传文件 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://stackoverflow.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= 将成为请求用户的习惯。

答案 4 :(得分:-2)

在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)