1

I am trying to create a simple web application with Flask. The goal is for a user to be able to upload a file (a .csv in my case), which then gets processed, and in the end the processed file should be saved to the user's computer.

So far, I am able to choose files from the browser and upload it. I save it as a Python object and I can also directly save it to my personal Downloads folder. However, I do not understand how I can make the download path dynamic. If I deploy the app, everybody should be able to have the processed file directly downloaded to their individual folder. How does that work?

Below is my code:

from flask import Flask, render_template, request, redirect
import os

app = Flask(__name__)


@app.route("/")
def home():
    return render_template("index.html")


@app.route("/upload-csv", methods=["GET", "POST"])
def uplaod_csv():

    if request.method == "POST":

        if  request.files:

            csv = request.files["csv"]

            path = "this should be dynamic"
            csv.save(os.path.join(path, csv.filename))

            return redirect(request.url)

    return render_template("index.html")

if __name__ == "__main__":
    app.run(debug=True)

I am rather new to web development, any kind of help is greatly appreciated!

snowe
  • 31
  • 1
  • 1
  • 4
  • By dynamic you mean something like "/users_data//..."? – Mihail Feraru Jul 21 '20 at 11:52
  • yes, as in: I cannot assume that everybody has the same folder structure so how do I make sure that the files are saved to a location that is present on the user's disk :) – snowe Jul 21 '20 at 11:54
  • I think you are making a confusion. Your route receives a file from the user and saves it on the server's disk. – Mihail Feraru Jul 21 '20 at 12:05

2 Answers2

1

I guess there is a misunderstanding.

csv.save(os.path.join(path, csv.filename)) - this saves the file on the server - when you develop the app, that is your computer.

This is not the path for the user.

The user's path is a setting in the user's browser. Usually it is the "downloads" directory or the user gets asked where to save the download.

Jürgen Gmach
  • 5,366
  • 3
  • 20
  • 37
  • so the idea would be to set 'path' to a location inside my app folder structure? that way it would be saved there and then the user can download it from there? – snowe Jul 21 '20 at 12:17
  • Either this way, or you do not save the file on the server at all, and just work on it or convert it, and immediately let the user download it again. This depends on your use case. – Jürgen Gmach Jul 21 '20 at 13:35
  • I am a bit confused to be honest. When a user uploads a file, I can save it as a Python object and work on it. But how do I make it 'downloadable'? You mention that I don't have to save it on the server at all, which sounds very convenient. – snowe Jul 22 '20 at 08:26
  • Does this answer your question? https://stackoverflow.com/questions/35710361/python-flask-send-file-stringio-blank-files If not - just save the file and do a `send_file` or a `send_from_directory`. As some clever guy said.. "Make it work, make it right, make it fast" – Jürgen Gmach Jul 22 '20 at 13:29
  • thank you for the link! :) it does help but I am still stuck/confused. The way I see it, the csv I upload gets converted to a filestorage object. I can use the method save to save it to a folder but I cannot use the method save if its not a filestorage object. I amm not sure how to convert a filestorage object into a pandas dataframe and then back to a filestorage object again. – snowe Jul 23 '20 at 10:13
  • Once you converted it to a Pandas dataframe or anything else, you do not convert it back to a filestorage object. This is just a helper to manage a file uploaded via a form. Once you have processed your file, you either store in on the server via regular file open function, or you directly make it available for download via the above mentioned functions. – Jürgen Gmach Jul 23 '20 at 12:54
1

I managed to get a version that behaves the way I want to:

from flask import Flask, render_template, request, redirect, url_for, send_from_directory
import os
import pandas as pd

app = Flask(__name__)

@app.route("/")
def home():
    return render_template("index.html")


@app.route("/upload-csv", methods=["GET", "POST"])
def upload_csv():

    if request.method == "POST":

        if request.files:

            csv_upload = request.files["csv"]
            filename = csv_upload.filename
            csv_upload.save(os.path.join("uploads", csv_upload.filename))

            path = os.path.join("uploads", csv_upload.filename)
            df = pd.read_csv(path)
            new_column = range(12343)
            df['new_column'] = new_column
            df.to_csv(os.path.join("downloads", filename))

            return redirect(url_for('uploaded_file', filename=filename))

    return render_template("index.html")


@app.route("/uploads/<filename>")
def uploaded_file(filename):
    filename_processed = 'processed' + '-' + filename
    return send_from_directory("downloads", filename, as_attachment=True, attachment_filename=filename_processed)


if __name__ == "__main__":
    app.run(debug=True)

I can now upload a csv, add a new column, and download it again. Pretty sure there are way better options to do that, but it works :) I first save the requested file in the folder "uploads", then read it from there as a pandas dataframe and do some basic manipulations for testing, then I save it as a csv to a folder called "downloads". From there, I can download it as an attachment.

Thank you for all the inputs!

snowe
  • 31
  • 1
  • 1
  • 4