python richiede il caricamento del file


123

Sto eseguendo una semplice operazione di caricamento di un file utilizzando la libreria di richieste Python. Ho cercato Stack Overflow e nessuno sembrava avere lo stesso problema, vale a dire che il file non viene ricevuto dal server:

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)

Sto riempiendo il valore della parola chiave 'upload_file' con il mio nome file, perché se lo lascio vuoto, dice

Error - You must select a file to upload!

E ora capisco

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

Che viene visualizzato solo se il file è vuoto. Quindi sono bloccato su come inviare il mio file con successo. So che il file funziona perché se vado su questo sito e compilo manualmente il modulo restituisce una bella lista di oggetti abbinati, che è quello che sto cercando. Apprezzerei davvero tutti i suggerimenti.

Alcuni altri thread correlati (ma non rispondono al mio problema):

Risposte:


210

Se upload_filedeve essere il file, usa:

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

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

e requestsinvierà un corpo POST del modulo in più parti con il upload_filecampo impostato sul contenuto del file.txtfile.

Il nome del file verrà incluso nell'intestazione MIME per il campo specifico:

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

Nota il filename="file.txt"parametro.

È possibile utilizzare una tupla per il filesvalore di mappatura, con un numero di elementi compreso tra 2 e 4, se è necessario un maggiore controllo. Il primo elemento è il nome del file, seguito dal contenuto e da un valore di intestazione del tipo di contenuto facoltativo e una mappatura facoltativa di intestazioni aggiuntive:

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

Questo imposta un nome file e un tipo di contenuto alternativi, tralasciando le intestazioni facoltative.

Se intendi che l' intero corpo del POST deve essere preso da un file (senza altri campi specificati), non utilizzare il filesparametro, invia semplicemente il file direttamente come data. Quindi potresti voler impostare Content-Typeanche un'intestazione, poiché nessuna verrà impostata altrimenti. Vedi Richieste Python - Dati POST da un file .


Ciao, come posso inviare più file condividendo lo stesso nome? Come "attaccamento" per esempio.
William Wino

4
@William: è possibile utilizzare una sequenza di tuple 2 a valore troppo, che consente i nomi dei campi che si ri-uso: files = [('attachment', open('attachment1.txt', 'rb')), ('attachment', open('attachment2.txt', 'rb'))]. Ogni tupla è una coppia di chiave e valore.
Martijn Pieters

2
Inoltre puoi anche usare files={'file':('nameoffile',open('namoffile','rb'),'Content-Type':'text/html','other header'),'file2':('nameoffile2',open('nameoffile2','rb'),'Content-Type':'application/xml','other header')}ma Se files = {} è usato allora headers = {'Content-Type': 'blah blah'} non deve essere usato! -> @ martijn-pieters: perché il tipo di contenuto multipart / form-data deve includere il valore limite utilizzato per delineare le parti nel corpo del post. La mancata impostazione dell'intestazione Content-Type garantisce che le richieste lo impostino sul valore corretto.
zaki

1
@MartijnPieters Non rischia di far trapelare il file? Lo requestschiude?
Matt Messersmith

4
@ MattMessersmith: no, non è chiuso. Se vuoi chiudere il file, usa with open(...) as fobj:e usa fobjnel filesmapping.
Martijn Pieters

35

(2018) la nuova libreria di richieste python ha semplificato questo processo, possiamo usare la variabile 'files' per segnalare che vogliamo caricare un file codificato in più parti

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

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

3
La libreria delle richieste chiude automaticamente il file?
Demetris

1
ciao, è passato del tempo da quando ho usato questa libreria. bella domanda. potresti dare una mano a me e agli altri digitando lsof | grep "nomefile" e condividere i risultati con noi? grazie :)
laycat

1
Con l'uso di lsof, sembra che il file rimanga aperto, o almeno, è così che interpreto i seguenti risultati. Prima, eseguendo il opennon vi è alcun record nella lsoftabella relativo al filename. Quindi, dopo che openè stato eseguito, vengono visualizzati più record con readaccesso. Dopo aver eseguito il requests.post, i record sono ancora lì a indicare che il file non è stato chiuso.
Demetris

23

Caricamento client

Se desideri caricare un singolo file con la requestslibreria Python , richiede che lib supporti i caricamenti in streaming , che ti consentono di inviare file o flussi di grandi dimensioni senza leggere in memoria .

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

Lato server

Quindi memorizzare il file sul server.pylato in modo tale da salvare il flusso in file senza caricarlo nella memoria. Di seguito è riportato un esempio con l'utilizzo dei caricamenti di file 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

Oppure usa werkzeug Form Data Parsing come menzionato in una correzione per il problema dei " caricamenti di file di grandi dimensioni che consumano memoria " per evitare di utilizzare la memoria in modo inefficiente durante il caricamento di file di grandi dimensioni (file da 22 GiB in ~ 60 secondi. L'utilizzo della memoria è costante a circa 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

In Ubuntu puoi applicare in questo modo,

per salvare il file in una posizione (temporanea) e quindi aprirlo e inviarlo all'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)

qual è il valore della datavariabile?
am.rez

può essere qualsiasi cosa come il nome utente, ho appena mostrato come caricare i file su
API
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.