Téléchargements de fichiers

Syntaxe

  • request.files[’nom’] # seul fichier requis
  • request.files.get(’name’) # Aucun si non posté
  • request.files.getlist(’name’) # liste de zéro ou plusieurs fichiers postés
  • CombinedMultiDict((request.files, request.form)) # combiner les données du formulaire et du fichier

Téléchargement de fichiers

Formulaire HTML

  • Utilisez une entrée de type [file] [1] et le navigateur fournira un champ permettant à l’utilisateur de sélectionner un fichier à télécharger.
  • Seuls les formulaires avec la méthode “post” peuvent envoyer des données de fichier.
  • Assurez-vous de définir l’attribut enctype=multipart/form-data du formulaire. Sinon, le nom du fichier sera envoyé mais pas les données du fichier.
  • Utilisez l’attribut multiple sur l’entrée pour permettre la sélection de plusieurs fichiers pour le champ unique.
<form method=post enctype=multipart/form-data>
    <!-- single file for the "profile" field -->
    <input type=file name=profile>
    <!-- multiple files for the "charts" field -->
    <input type=file multiple name=charts>
    <input type=submit>
</form>

Requêtes Python

[Requests][2] est une puissante bibliothèque Python pour effectuer des requêtes HTTP. Vous pouvez l’utiliser (ou d’autres outils) pour [publier des fichiers][3] sans navigateur.

  • Ouvrir les fichiers à lire en mode binaire.
  • Il existe plusieurs structures de données que files prend. Cela montre une liste de tuples (name, data), qui autorise plusieurs fichiers comme le formulaire ci-dessus.
import requests

with open('profile.txt', 'rb') as f1, open('chart1.csv', 'rb') as f2, open('chart2.csv', 'rb') as f3:
    files = [
        ('profile', f1),
        ('charts', f2),
        ('charts', f3)
    ]
    requests.post('http://localhost:5000/upload', files=files)

Cette liste n’est pas censée être exhaustive. Pour des exemples utilisant votre outil préféré ou des scénarios plus complexes, consultez la documentation de cet outil.

[1] : https://developer.mozilla.org/en-US/docs/Web/HTML/Element/Input [2] : http://docs.python-requests.org/en/latest/ [3] : http://docs.python-requests.org/en/master/user/quickstart/#post-a-multipart-encoded-file

Transmission de données à WTForms et Flask-WTF

WTForms fournit un FileField pour rendre une entrée de type de fichier. Il ne fait rien de spécial avec les données téléchargées. Cependant, étant donné que Flask divise les données du formulaire (request.form) et les données du fichier (request.files), vous devez vous assurer de transmettre les données correctes lors de la création du formulaire. Vous pouvez utiliser un CombinedMultiDict pour combiner les deux en une seule structure que WTForms comprend.

form = ProfileForm(CombinedMultiDict((request.files, request.form)))

Si vous utilisez [Flask-WTF][1], une extension pour intégrer Flask et WTForms, la transmission des données correctes sera gérée automatiquement pour vous.

En raison d’un bogue dans WTForms, un seul fichier sera présent pour chaque champ, même si plusieurs ont été téléchargés. Voir [ce numéro][2] pour plus de détails. Il sera corrigé dans la version 3.0.

[1] : http://flask-wtf.readthedocs.io/en/latest/ [2] : https://github.com/wtforms/wtforms/pull/281

Enregistrer les téléchargements sur le serveur

Les fichiers téléchargés sont disponibles dans request.files, un [MultiDict][1] mappant les noms de champs aux objets de fichiers. Utilisez getlist - au lieu de [] ou get - si plusieurs fichiers ont été téléchargés avec le même nom de champ.

request.files['profile']  # single file (even if multiple were sent)
request.files.getlist('charts')  # list of files (even if one was sent)

Les objets dans request.files ont une méthode save qui enregistre le fichier localement. Créez un répertoire commun pour y enregistrer les fichiers.

L’attribut filename est le nom avec lequel le fichier a été téléchargé. Cela peut être défini arbitrairement par le client, alors passez-le via la méthode secure_filename pour générer un nom valide et sûr sous lequel enregistrer. Cela ne garantit pas que le nom est unique, donc les fichiers existants seront écrasés à moins que vous ne fassiez un travail supplémentaire pour le détecter.

import os
from flask import render_template, request, redirect, url_for
from werkzeug import secure_filename

# Create a directory in a known location to save files to.
uploads_dir = os.path.join(app.instance_path, 'uploads')
os.makedirs(uploads_dir, exists_ok=True)

@app.route('/upload', methods=['GET', 'POST'])
def upload():
    if request.method == 'POST':
        # save the single "profile" file
        profile = request.files['profile']
        profile.save(os.path.join(uploads_dir, secure_filename(profile.filename)))

        # save each "charts" file
        for file in request.files.getlist('charts'):
            file.save(os.path.join(uploads_dir, secure_filename(file.name)))

        return redirect(url_for('upload'))

    return render_template('upload.html')

[1] : http://werkzeug.pocoo.org/docs/0.11/datastructures/#werkzeug.datastructures.MultiDict

PARSE CSV FILE UPLOAD COMME LISTE DE DICTIONNAIRES DANS FLASK SANS ENREGISTRER

Les développeurs ont souvent besoin de concevoir des sites Web permettant aux utilisateurs de télécharger un fichier CSV. Habituellement, il n’y a aucune raison de sauvegarder le fichier CSV réel puisque les données seront traitées et/ou stockées dans une base de données une fois téléchargées. Cependant, de nombreuses méthodes PYTHON, sinon la plupart, d’analyse des données CSV nécessitent que les données soient lues sous forme de fichier. Cela peut présenter un casse-tête si vous utilisez FLASK pour le développement Web.

Supposons que notre CSV comporte une ligne d’en-tête et ressemble à ceci :

h1,h2,h3
'yellow','orange','blue'
'green','white','black'
'orange','pink','purple'

Supposons maintenant que le formulaire html pour télécharger un fichier soit le suivant :

<form action="upload.html" method="post" enctype="multipart/form-data">
    <input type="file" name="fileupload" id="fileToUpload">
    <input type="submit" value="Upload File" name="submit">
</form>

Puisque personne ne veut réinventer la roue, vous décidez d’IMPORTER csv dans votre script FLASK. Il n’y a aucune garantie que les gens téléchargent le fichier csv avec les colonnes dans le bon ordre. Si le fichier csv a une ligne d’en-tête, alors avec l’aide de la méthode csv.DictReader vous pouvez lire le fichier CSV comme une liste de dictionnaires, indexés par les entrées dans la ligne d’en-tête. Cependant, csv.DictReader a besoin d’un fichier et n’accepte pas directement les chaînes. Vous pensez peut-être que vous devez utiliser les méthodes FLASK pour d’abord enregistrer le fichier téléchargé, obtenir le nouveau nom et emplacement du fichier, l’ouvrir à l’aide de csv.DictReader, puis supprimer le fichier. On dirait un peu de gaspillage.

Heureusement, nous pouvons obtenir le contenu du fichier sous forme de chaîne, puis diviser la chaîne en lignes terminées. La méthode csv csv.DictReader l’acceptera comme substitut d’un fichier. Le code suivant montre comment cela peut être accompli sans enregistrer temporairement le fichier.

@application.route('upload.html',methods = ['POST'])
def upload_route_summary():
    if request.method == 'POST':

        # Create variable for uploaded file
        f = request.files['fileupload']  

        #store the file contents as a string
        fstring = f.read()
        
        #create list of dictionaries keyed by header row
        csv_dicts = [{k: v for k, v in row.items()} for row in csv.DictReader(fstring.splitlines(), skipinitialspace=True)]

        #do something list of dictionaries
    return "success"

La variable csv_dicts est maintenant la liste de dictionnaires suivante :

   csv_dicts = 
    [
        {'h1':'yellow','h2':'orange','h3':'blue'},
        {'h1':'green','h2':'white','h3':'black'},
        {'h1':'orange','h2':'pink','h3':'purple'}
    ]

Si vous êtes nouveau sur PYTHON, vous pouvez accéder à des données comme celles-ci :

csv_dicts[1]['h2'] = 'white'
csv_dicts[0]['h3'] = 'blue'

D’autres solutions impliquent d’importer le module io et d’utiliser la méthode io.Stream. Je pense que c’est une approche plus directe. Je pense que le code est un peu plus facile à suivre que d’utiliser la méthode io. Cette approche est spécifique à l’exemple d’analyse d’un fichier CSV téléchargé.