web-dev-qa-db-fra.com

Téléchargement et téléchargement de fichiers avec Flask

J'essaie d'écrire une webapp vraiment simple avec PythonAnywhere et Flask qui a permis à l'utilisateur de télécharger un fichier texte, génère un fichier csv, puis permet à l'utilisateur de télécharger le fichier csv. Il ne fait pas '' t doivent être fantaisistes, cela ne peut que fonctionner. J'ai déjà écrit le programme pour générer le csv à partir d'un fichier txt sur le lecteur.

En ce moment, ma fonction ouvre le fichier sur le lecteur avec:

with open(INPUTFILE, "r") as fname:

et écrit le csv avec:

with open(OUTPUTFILE, 'w') as fname:

iNPUTFILE et OUTPUTFILE étant des chaînes de nom de fichier.

Serait-il préférable pour moi de gérer les fichiers comme des objets, retournés par le flacon/html d'une manière ou d'une autre?

Je ne sais pas comment faire ça. Comment dois-je structurer ce programme? De combien de modèles HTML ai-je besoin? Je préférerais travailler sur les fichiers sans les enregistrer n'importe où, mais si je dois les enregistrer dans le répertoire PythonAnywhere, je le pourrais. Comment puis je faire ça?

24
User1996

PythonAnywhere dev ici. C'est une bonne question sur Flask et le développement web en général plutôt que spécifique à notre système, donc je vais essayer de donner une réponse générique sans rien de spécifique pour nous :-)

Il y a quelques choses que je devrais savoir pour donner une réponse définitive à votre question, alors je commencerai par énumérer les hypothèses que je fais - laissez-moi un commentaire si je me trompe avec l'une d'entre elles et Je mettrai à jour la réponse en conséquence.

  • Je suppose que les fichiers que vous téléchargez ne sont pas énormes et peuvent tenir dans une quantité raisonnable de mémoire - disons, plus petite qu'un mégaoctet.
  • Je suppose que le programme que vous avez déjà écrit pour générer le CSV à partir du fichier texte est en Python, et qu'il a (ou, peut-être plus probablement, pourrait facilement être modifié pour avoir) une fonction qui prend une chaîne contenant le contenu du fichier texte et renvoie le contenu qui doit être écrit dans le CSV.

Si les deux sont le cas, alors la meilleure façon de structurer votre application Flask serait de tout gérer dans Flask. Un exemple de code vaut mille mots, alors voici un simple que j'ai mis ensemble, ce qui permet à l'utilisateur de télécharger un fichier texte, l'exécute via une fonction appelée transform (qui est l'endroit où la fonction de votre programme de conversion serait inséré - le mien remplace simplement = avec , tout au long du fichier) et renvoie les résultats au navigateur. Il existe une version live de cette application sur PythonAnywhere ici .

from flask import Flask, make_response, request

app = Flask(__name__)

def transform(text_file_contents):
    return text_file_contents.replace("=", ",")


@app.route('/')
def form():
    return """
        <html>
            <body>
                <h1>Transform a file demo</h1>

                <form action="/transform" method="post" enctype="multipart/form-data">
                    <input type="file" name="data_file" />
                    <input type="submit" />
                </form>
            </body>
        </html>
    """

@app.route('/transform', methods=["POST"])
def transform_view():
    request_file = request.files['data_file']
    if not request_file:
        return "No file"

    file_contents = request_file.stream.read().decode("utf-8")

    result = transform(file_contents)

    response = make_response(result)
    response.headers["Content-Disposition"] = "attachment; filename=result.csv"
    return response

Concernant vos autres questions:

  • Modèles: Je n'ai pas utilisé de modèle pour cet exemple, car je voulais que tout s'intègre dans un seul morceau de code. Si je le faisais correctement, je mettrais les éléments générés par la vue form dans un modèle, mais c'est tout.
  • Pouvez-vous le faire en écrivant dans des fichiers - oui, vous pouvez, et le fichier téléchargé peut être enregistré en utilisant le save( nom de fichier ) méthode sur l'objet file dont j'utilise la propriété stream. Mais si vos fichiers sont assez petits (selon mon hypothèse ci-dessus), il est probablement plus logique de les traiter en mémoire comme le fait le code ci-dessus.

J'espère que tout aide, et si vous avez des questions, laissez simplement un commentaire.

31
Giles Thomas

Mieux vaut ajouter

response.headers["Cache-Control"] = "must-revalidate"
response.headers["Pragma"] = "must-revalidate"
response.headers["Content-type"] = "application/csv"

Si vous n'ajoutez pas le type de contenu, FF 48.0 l'a signalé en html et a ouvert la boîte de dialogue Enregistrer une fois pour HTML, puis pour CSV. Si vous n'ajoutez pas Cache-Control, votre résultat peut être mis en cache, et si vous diffusez du contenu actif, ce n'est pas ce que vous voulez. Si vous utilisez must-revalidate sans âge, il servira effectivement de no-cache - voir ici et ici pour une explication.

7
teosbpl