python demande le téléchargement de fichiers

125

J'effectue une tâche simple de téléchargement d'un fichier à l'aide de la bibliothèque de requêtes Python. J'ai cherché Stack Overflow et personne ne semblait avoir le même problème, à savoir que le fichier n'est pas reçu par le serveur:

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)

Je remplis la valeur du mot-clé 'upload_file' avec mon nom de fichier, car si je le laisse vide, il dit

Error - You must select a file to upload!

Et maintenant je reçois

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

Ce qui n'apparaît que si le fichier est vide. Je ne sais donc pas comment envoyer mon fichier avec succès. Je sais que le fichier fonctionne car si je vais sur ce site Web et que je remplis manuellement le formulaire, il renvoie une belle liste d'objets correspondants, ce que je recherche. J'apprécierais vraiment tous les indices.

Quelques autres threads liés (mais ne répondant pas à mon problème):

scichris
la source

Réponses:

211

Si upload_fileest censé être le fichier, utilisez:

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

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

et requestsenverra un corps POST de formulaire en plusieurs parties avec le upload_filechamp défini sur le contenu du file.txtfichier.

Le nom de fichier sera inclus dans l'en-tête mime du champ spécifique:

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

Notez le filename="file.txt"paramètre.

Vous pouvez utiliser un tuple pour la filesvaleur de mappage, avec entre 2 et 4 éléments, si vous avez besoin de plus de contrôle. Le premier élément est le nom de fichier, suivi du contenu, et une valeur d'en-tête de type de contenu facultative et un mappage facultatif d'en-têtes supplémentaires:

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

Cela définit un nom de fichier et un type de contenu alternatifs, en laissant de côté les en-têtes facultatifs.

Si vous voulez dire que tout le corps POST doit être extrait d'un fichier (sans aucun autre champ spécifié), n'utilisez pas le filesparamètre, publiez simplement le fichier directement sous data. Vous voudrez peut-être également définir un en- Content-Typetête, car aucun ne sera défini autrement. Voir Requêtes Python - Données POST à ​​partir d'un fichier .

Martijn Pieters
la source
Bonjour, Comment envoyer plusieurs fichiers partageant le même nom? Comme «attachement» par exemple.
William Wino
4
@William: vous pouvez utiliser une séquence de trop, ce qui vous permet de réutiliser les noms de champs tuples 2-valeur: files = [('attachment', open('attachment1.txt', 'rb')), ('attachment', open('attachment2.txt', 'rb'))]. Chaque tuple est une paire de clé et de valeur.
Martijn Pieters
2
Vous pouvez également utiliser files={'file':('nameoffile',open('namoffile','rb'),'Content-Type':'text/html','other header'),'file2':('nameoffile2',open('nameoffile2','rb'),'Content-Type':'application/xml','other header')}mais si files = {} est utilisé alors headers = {'Content-Type': 'blah blah'} ne doit pas être utilisé! -> @ martijn-pieters: car le Content-Type multipart / form-data doit inclure la valeur limite utilisée pour délimiter les parties dans le corps de l'article. Le fait de ne pas définir l'en-tête Content-Type garantit que les requêtes le définissent sur la valeur correcte.
zaki
1
@MartijnPieters Est-ce que cela ne risque pas de fuir le fichier? Le requestsferme- t- il?
Matt Messersmith
4
@MattMessersmith: non, ce n'est pas fermé. Si vous souhaitez fermer le fichier, utilisez with open(...) as fobj:et utilisez fobjdans le filesmappage.
Martijn Pieters
36

(2018) la nouvelle bibliothèque de requêtes python a simplifié ce processus, nous pouvons utiliser la variable 'files' pour signaler que nous voulons télécharger un fichier codé en plusieurs parties

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

r = requests.post(url, files=files)
r.text
laycat
la source
3
La bibliothèque de demandes ferme-t-elle automatiquement le fichier?
Demetris
1
bonjour, ça fait un moment que je n'ai pas utilisé cette bibliothèque. bonne question. pourriez-vous me donner un coup de main et aux autres en tapant lsof | grep "filename" et partager vos résultats avec nous? merci :)
laycat
1
Avec l'utilisation de lsof, il semble que le fichier reste ouvert, ou du moins, c'est ainsi que j'interprète les résultats suivants. Avant, en exécutant le, openil n'y a pas d'enregistrement dans la lsoftable sur le filename. Ensuite, après l' openexécution de, plusieurs enregistrements apparaissent avec readaccès. Après avoir exécuté le requests.post, les enregistrements sont toujours là, indiquant que le fichier ne s'est pas fermé.
Demetris
23

Téléchargement client

Si vous souhaitez télécharger un seul fichier avec la requestsbibliothèque Python , alors requests lib prend en charge les téléchargements en continu , qui vous permettent d' envoyer de gros fichiers ou des flux sans lecture en mémoire .

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

Du côté serveur

Ensuite, stockez le fichier sur le server.pycôté de manière à enregistrer le flux dans un fichier sans le charger dans la mémoire. Voici un exemple d'utilisation des téléchargements de fichiers 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

Ou utilisez l' analyse des données de formulaire werkzeug comme mentionné dans un correctif pour le problème des « téléchargements de fichiers volumineux qui consomment de la mémoire » afin d' éviter d'utiliser la mémoire de manière inefficace sur le téléchargement de fichiers volumineux (fichier de 22 Gio en ~ 60 secondes. L'utilisation de la mémoire est constante à environ 13 Mio.).

@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
la source
0

Dans Ubuntu, vous pouvez appliquer de cette façon,

pour enregistrer le fichier à un endroit (temporaire), puis ouvrez-le et envoyez-le à l'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)
Harshit Trivedi
la source
quelle est la valeur de la datavariable?
am.rez le
cela peut être n'importe quoi comme un nom d'utilisateur, je viens de montrer comment télécharger des fichiers vers des
API