Python fordert das Hochladen von Dateien an

122

Ich führe eine einfache Aufgabe zum Hochladen einer Datei mithilfe der Python-Anforderungsbibliothek aus. Ich habe nach Stack Overflow gesucht und niemand schien das gleiche Problem zu haben, nämlich dass die Datei nicht vom Server empfangen wird:

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)

Ich fülle den Wert des Schlüsselworts 'upload_file' mit meinem Dateinamen, denn wenn ich ihn leer lasse, heißt es

Error - You must select a file to upload!

Und jetzt verstehe ich

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

Was nur angezeigt wird, wenn die Datei leer ist. Ich bin also nicht sicher, wie ich meine Datei erfolgreich senden soll. Ich weiß, dass die Datei funktioniert, denn wenn ich auf diese Website gehe und das Formular manuell ausfülle, wird eine schöne Liste übereinstimmender Objekte zurückgegeben, nach der ich suche. Ich würde mich wirklich über alle Hinweise freuen.

Einige andere Themen im Zusammenhang (aber nicht mein Problem zu beantworten):

Scichris
quelle

Antworten:

210

Wenn upload_filees sich um die Datei handeln soll, verwenden Sie:

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

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

und requestssendet einen mehrteiligen POST-Textkörper, dessen upload_fileFeld auf den Inhalt der file.txtDatei festgelegt ist.

Der Dateiname wird in den MIME-Header für das jeweilige Feld aufgenommen:

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

Beachten Sie das filename="file.txt" Parameter.

Sie können ein Tupel für den filesZuordnungswert mit 2 bis 4 Elementen verwenden, wenn Sie mehr Kontrolle benötigen. Das erste Element ist der Dateiname, gefolgt vom Inhalt, einem optionalen Headerwert für den Inhaltstyp und einer optionalen Zuordnung zusätzlicher Header:

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

Dadurch werden ein alternativer Dateiname und Inhaltstyp festgelegt, wobei die optionalen Header weggelassen werden.

Wenn Sie meinen, dass der gesamte POST-Body aus einer Datei entnommen werden soll (ohne dass andere Felder angegeben sind), verwenden Sie den filesParameter nicht, sondern veröffentlichen Sie die Datei direkt als data. Möglicherweise möchten Sie dann auch einen Content-TypeHeader festlegen , da sonst keiner festgelegt wird. Siehe Python-Anforderungen - POST-Daten aus einer Datei .

Martijn Pieters
quelle
Hallo, wie sende ich mehrere Dateien mit demselben Namen? Wie zum Beispiel "Anhang".
William Wino
4
@William: Sie können auch eine Folge von 2-Wert-Tupeln verwenden, mit denen Sie Feldnamen wiederverwenden können : files = [('attachment', open('attachment1.txt', 'rb')), ('attachment', open('attachment2.txt', 'rb'))]. Jedes Tupel ist ein Paar aus Schlüssel und Wert.
Martijn Pieters
2
Sie können auch verwenden, files={'file':('nameoffile',open('namoffile','rb'),'Content-Type':'text/html','other header'),'file2':('nameoffile2',open('nameoffile2','rb'),'Content-Type':'application/xml','other header')}aber wenn files = {} verwendet wird, dürfen headers = {'Content-Type': 'bla bla'} nicht verwendet werden! -> @ martijn-pieters: Weil der Inhaltstyp für mehrteilige / Formulardaten den Grenzwert enthalten muss, der zur Abgrenzung der Teile im Post-Body verwendet wird. Wenn Sie den Content-Type-Header nicht setzen, wird er durch Anforderungen auf den richtigen Wert gesetzt.
Zaki
1
@MartijnPieters Ist dies nicht das Risiko, dass die Datei verloren geht? Schließt requestses?
Matt Messersmith
4
@ MattMessersmith: Nein, es ist nicht geschlossen. Wenn Sie die Datei schließen möchten, verwenden Sie with open(...) as fobj:und verwenden Sie fobjin der filesZuordnung.
Martijn Pieters
34

(2018) Die neue Python-Anforderungsbibliothek hat diesen Prozess vereinfacht. Mit der Variablen 'files' können wir signalisieren, dass wir eine mehrteilig codierte Datei hochladen möchten

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

r = requests.post(url, files=files)
r.text
Laienkatze
quelle
3
Schließt die Anforderungsbibliothek die Datei automatisch?
Demetris
1
Hallo, es ist schon eine Weile her, seit ich diese Bibliothek benutzt habe. gute Frage. Könnten Sie mir und den anderen helfen, indem Sie lsof | eingeben? grep "Dateiname" und teilen Sie Ihre Ergebnisse mit uns? danke :)
laycat
1
Bei Verwendung von lsofscheint die Datei offen zu bleiben, oder zumindest interpretiere ich die folgenden Ergebnisse so. Vor dem Ausführen von opengibt es in der lsofTabelle keinen Datensatz über das filename. Nachdem das openausgeführt wurde, werden mehrere Datensätze mit readZugriff angezeigt. Nach dem Ausführen von requests.postsind die Datensätze noch vorhanden, um anzuzeigen, dass die Datei nicht geschlossen wurde.
Demetris
23

Client-Upload

Wenn Sie eine einzelne Datei mit der Python- requestsBibliothek hochladen möchten, unterstützt request lib Streaming-Uploads , mit denen Sie große Dateien oder Streams senden können, ohne in den Speicher zu lesen .

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

Serverseite

Speichern Sie die Datei dann so auf der server.pySeite, dass der Stream in einer Datei gespeichert wird, ohne in den Speicher geladen zu werden. Im Folgenden finden Sie ein Beispiel für die Verwendung von Uploads von Flask-Dateien .

@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

Oder nutzen werkzeug Daten Parsing wie in einer Lösung für das Problem der „erwähnt große Datei - Uploads auffressen Speicher “ , um zu vermeiden Speicher mit ineffizienten auf große Dateien hochladen (st 22 GiB Datei in ~ 60 Sekunden. Der Speicherverbrauch ist konstant bei etwa 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
Gihanchanuka
quelle
0

In Ubuntu können Sie auf diese Weise anwenden,

um die Datei an einem Ort (vorübergehend) zu speichern und sie dann zu öffnen und an die API zu senden

      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)
Harshit Trivedi
quelle
Was ist der Wert der dataVariablen?
am.rez
Es kann alles wie ein Benutzername sein. Ich habe gerade gezeigt, wie man Dateien auf REST apis
hochlädt