python yêu cầu tải lên tệp


123

Tôi đang thực hiện một tác vụ đơn giản là tải lên tệp bằng thư viện yêu cầu Python. Tôi đã tìm kiếm Stack Overflow và dường như không ai gặp phải vấn đề tương tự, cụ thể là máy chủ không nhận được tệp:

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)

Tôi đang điền giá trị của từ khóa 'upload_file' bằng tên tệp của mình, vì nếu tôi để trống, nó sẽ nói

Error - You must select a file to upload!

Và bây giờ tôi nhận được

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

Điều này chỉ xuất hiện khi tệp trống. Vì vậy, tôi bị mắc kẹt không biết làm thế nào để gửi tệp của mình thành công. Tôi biết rằng tệp hoạt động vì nếu tôi truy cập trang web này và điền vào biểu mẫu theo cách thủ công, nó sẽ trả về một danh sách đẹp các đối tượng phù hợp, đó là những gì tôi đang theo đuổi. Tôi thực sự đánh giá cao tất cả các gợi ý.

Một số chủ đề khác có liên quan (nhưng không trả lời vấn đề của tôi):

Câu trả lời:


210

Nếu upload_fileđược định nghĩa là tệp, hãy sử dụng:

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

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

requestssẽ gửi upload_filenội dung POST của biểu mẫu gồm nhiều phần với trường được đặt thành nội dung của file.txttệp.

Tên tệp sẽ được bao gồm trong tiêu đề kịch câm cho trường cụ thể:

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

Lưu ý filename="file.txt"tham số.

Bạn có thể sử dụng một bộ filesgiá trị ánh xạ, có từ 2 đến 4 phần tử, nếu bạn cần kiểm soát nhiều hơn. Phần tử đầu tiên là tên tệp, tiếp theo là nội dung và giá trị tiêu đề loại nội dung tùy chọn và ánh xạ tùy chọn của các tiêu đề bổ sung:

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

Điều này đặt tên tệp và loại nội dung thay thế, loại bỏ các tiêu đề tùy chọn.

Nếu bạn có nghĩa là toàn bộ nội dung POST được lấy từ một tệp (không có trường nào khác được chỉ định), thì không sử dụng filestham số, chỉ đăng tệp trực tiếp dưới dạng data. Sau đó, bạn cũng có thể muốn đặt Content-Typetiêu đề, vì không có tiêu đề nào sẽ được đặt nếu không. Xem yêu cầu Python - ĐĂNG dữ liệu từ một tệp .


Xin chào, Làm cách nào để gửi nhiều tệp có cùng tên? Như 'tập tin đính kèm' chẳng hạn.
William Wino

4
@William: bạn có thể sử dụng một chuỗi các tuples 2 giá trị quá, cho phép tên trường bạn sử dụng lại: files = [('attachment', open('attachment1.txt', 'rb')), ('attachment', open('attachment2.txt', 'rb'))]. Mỗi bộ giá trị là một cặp khóa và giá trị.
Martijn Pieters

2
Ngoài ra, bạn cũng có thể sử dụng files={'file':('nameoffile',open('namoffile','rb'),'Content-Type':'text/html','other header'),'file2':('nameoffile2',open('nameoffile2','rb'),'Content-Type':'application/xml','other header')}nhưng Nếu tệp = {} được sử dụng thì không được sử dụng headers = {'Content-Type': 'blah blah'}! -> @ martijn-pieters: vì đa phần / biểu mẫu-dữ liệu Nội dung-Loại phải bao gồm giá trị ranh giới được sử dụng để phân định các phần trong nội dung bài đăng. Việc không đặt tiêu đề Loại-Nội dung đảm bảo rằng các yêu cầu sẽ đặt nó thành giá trị chính xác.
zaki

1
@MartijnPieters Điều này có nguy cơ làm rò rỉ tệp không? Có requestsđóng nó không?
Matt Messersmith

4
@MattMessersmith: không, nó chưa đóng. Nếu bạn muốn đóng tệp, hãy sử dụng with open(...) as fobj:và sử dụng fobjtrong filesánh xạ.
Martijn Pieters

35

(2018) thư viện yêu cầu python mới đã đơn giản hóa quá trình này, chúng tôi có thể sử dụng biến 'tệp' để báo hiệu rằng chúng tôi muốn tải lên tệp được mã hóa nhiều phần

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

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

3
Thư viện yêu cầu có tự động đóng tệp không?
Demetris

1
xin chào, đã lâu rồi tôi chưa sử dụng thư viện này. câu hỏi hay. bạn có thể giúp tôi và những người khác một tay bằng cách gõ lsof | grep "tên tệp" và chia sẻ kết quả của bạn với chúng tôi? cảm ơn :)
laycat

1
Với việc sử dụng lsof, có vẻ như tệp vẫn mở, hoặc ít nhất, đây là cách tôi diễn giải các kết quả sau. Trước khi chạy, openkhông có bản ghi nào trong lsofbảng về filename. Sau đó, sau khi openđược thực thi, nhiều bản ghi xuất hiện với readquyền truy cập. Sau khi thực hiện requests.post, các bản ghi vẫn ở đó cho thấy rằng tệp đã không đóng.
Demetris

23

Tải lên từ khách hàng

Nếu bạn muốn tải lên một tệp duy nhất với requeststhư viện Python , thì các yêu cầu lib hỗ trợ tải lên trực tuyến , cho phép bạn gửi các tệp hoặc luồng lớn mà không cần đọc vào bộ nhớ .

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

Phía máy chủ

Sau đó, lưu trữ tệp ở server.pybên cạnh để lưu luồng thành tệp mà không cần tải vào bộ nhớ. Sau đây là một ví dụ với việc sử dụng tải lên tệp Flask .

@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

Hoặc sử dụng phân tích cú pháp dữ liệu biểu mẫu werkzeug như đã đề cập trong bản sửa lỗi cho vấn đề " tải lên tệp lớn ngốn bộ nhớ " để tránh sử dụng bộ nhớ không hiệu quả khi tải lên tệp lớn ( tệp st 22 GiB trong ~ 60 giây. Bộ nhớ sử dụng không đổi vào khoảng 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

0

Trong Ubuntu bạn có thể áp dụng cách này,

để lưu tệp tại một số vị trí (tạm thời), sau đó mở và gửi tệp tới 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)

giá trị của databiến là gì?
am.rez

nó có thể là bất cứ thứ gì như tên người dùng, tôi vừa chỉ cách tải tệp lên REST apis
Harshit Trivedi
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.