I'm building a small app that will (eventually):
Upload a file + process it with a background analysis tool + spit it back out to the user that uploaded it. I'm trying to put each file into a randomly generated folder name to keep things categorized a bit.
To start with, I'm getting the upload redirects working, and they're almost there but I can't seem to get flask to redirect to a randomly generated folder name, even though it exists and the file the user has uploaded is in it.
Here's how I'm accomplishing this...
#app.route('/upload', methods=['POST'])
def upload():
file = request.files['file']
if file and allowed_file(file.filename):
filename = secure_filename(file.filename)
foldername = tempfile.mkdtemp(prefix='file', dir='uploads')
file.save(os.path.join(foldername, filename))
return redirect(url_for('uploaded_file', foldername=foldername, filename=filename))
else:
return '<h3>Invalid File, PDF or image only.</h3>'
#app.route('/<foldername>/<filename>')
def uploaded_file(foldername, filename):
return send_from_directory(filename)
On the redirect, the file is in the right place, and the folder is in the right place, and the browser is headed to the right place, but flask 404s it...
192.168.1.69 - - [29/Jan/2017 22:32:19] "GET / HTTP/1.1" 200 -
192.168.1.69 - - [29/Jan/2017 22:32:25] "POST /upload HTTP/1.1" 302 -
192.168.1.69 - - [29/Jan/2017 22:32:25] "GET /uploads/file0wr5ug4y/Screen_Shot_2017-01-25_at_11.23.48_AM.png HTTP/1.1" 404 -
Disregard the last comment to the first string of replies, trial and error figured it out.
The foldername must be passed to the send_from_directory function as well, and more importantly must be prefixed with 'path:' to allow the filename variable value to be a path.
So the final route looks like...
#app.route('/<foldername>/<path:filename>')
def uploaded_file(foldername, filename):
return send_from_directory(foldername, filename)
Related
I am writing a simple PDF converter inside container. Send docx and get PDF, but I don't want file to stay on server, therefor I wish to delete them after download request.
I tried to use flask after_this_request on get request on Download(Resource)
class Downloader(Resource):
def get(self, doc_id):
folder, file_name = FileConverter.download_file(doc_id)
if not folder:
return jsonify({"status": "NOTOK", "error": "No File"})
#after_this_request
def _clean_file():
FileConverter.delete_file(doc_id)
return send_from_directory(folder, file_name, as_attachment=True)
FileConverter.delete_file checks if file exists and uses os.remove to delete it, however this part of code corrupt PDF into unreadable. If I remove #after_this_request, I get working PDF.
How should I do this?
I had this problem and I solved it like this:
class Downloader(Resource):
def get(self, doc_id):
folder, file_name = FileConverter.download_file(doc_id)
if not folder:
return jsonify({"status": "NOTOK", "error": "No File"})
try:
return send_from_directory(folder, file_name, as_attachment=True)
finally:
FileConverter.delete_file(doc_id)
I am using a Flask application to update some PDF files, convert them to an Excel file and send this file back to the user. I am using an instance folder to store the pdf and the excel files.
But when the user press the button "Download" in order to download the generated Excel file, an old file is downloaded (from an older session).
Moreover, when I try to change my code, for example, I changed the name of this Excel file: I can see the new name in the instance folder, but when I download the file with the webapp, it is still the old name (and old file). I have no idea where the webapp is looking for this old file...
MEDIA_FOLDER = '/media/htmlfi/'
app = Flask(__name__)
app.config.from_object(Config)
INSTANCE_FOLDER = app.instance_path
app.config['UPLOAD_FOLDER'] = INSTANCE_FOLDER+MEDIA_FOLDER
#app.route('/file/')
def send():
folder = app.config['UPLOAD_FOLDER']
try:
return send_file(folder+ "file.xlsx", as_attachment=True)
finally:
os.remove(folder+ "file.xlsx")
<a href="{{ url_for('send') }}" ><button class='btn btn-default'>DOWNLOAD</button></a>
I am really new to webapp in general, thank you for your help :)
send_file takes a cache_timeout parameter which is the number of seconds you want to cache the download. By default is 12 hours.
return send_file(
file.file_path(),
as_attachment=True,
cache_timeout=app.config['FILE_DOWNLOAD_CACHE_TIMEOUT'],
attachment_filename=file.file_name
)
http://flask.pocoo.org/docs/1.0/api/
I have some problems with file serving with flask.
Firstly, my file tree:
processedMain
--processed1
--processed2
--...
--processedN
In each folder I have bunch of files that I represent it all in table where i put href link with path of each of files and pass it through route function with send_file return.
The problem is that the only way that I was able to make it work is to creating #app.route for every subfolder hardcoded, like this:
#app.route('/' + pathFits1[1] + '<id>', methods=['GET'])
def returnImg1(id):
return send_file(pathFits1[1] + id, as_attachment=True, attachment_filename=id)
#app.route('/' + pathFits1[2] +'<id>', methods=['GET'])
def returnImg2(id):
return send_file(pathFits1[2] + id, as_attachment=True, attachment_filename=id)
#app.route('/' + pathFits1[3] + '<id>', methods=['GET'])
def returnImg3(id):
return send_file(pathFits1[3] + id, as_attachment=True, attachment_filename=id)
where pathFits1[i] is path to the subfolder and id is passed when user clicks on file in web table.
Problem is that I have a lot of subfolder, and its tiring to create #app.route for each.
Is this possible to create just one route for every thing?
P.S.
I'm quite new to flask, so if I forgot to tell something, please tell me I will fill it out.
Solution
Actually it was embarrassingly simple.
As #Dan.D pointed, flask has convertors of captured variables, for me it was all I needed, as my id variable already contains full path to file.
So now it's just this 3 lines which substitute a whole bunch or duplicated code:
#app.route('/<path:id>')
def returnImg(id):
fileName = id.rsplit('/', 1)[-1]
return send_file(id, as_attachment=True, attachment_filename=fileName)
Where id is full path to file, path: is converter to path and fileName is name of file that user will download without path in it (all after last slash)
You can capture the path in the route expression. From the documentation:
#app.route('/path/<path:subpath>')
def show_subpath(subpath):
# show the subpath after /path/
return 'Subpath %s' % subpath
From http://flask.pocoo.org/docs/1.0/quickstart/
As far as I know, Flask doesn't support multiple variable sections in #app.route URLs. However, you could set the full path of the file as a variable section and then parse it to extract the sub-folder path and the file ID.
Here's a working example:
pathFits1 = [None] * 3
pathFits1[1] = 'content/images1/'
pathFits1[2] = 'content/images2/'
#app.route('/<path:fullpath>', methods=['GET'])
def returnImg(fullpath):
global pathFits1
print("--> GET request: ", fullpath)
# parse the full path to extract the subpath and ID
# subpath: get everything until the last slash
subpath = fullpath.rsplit('/', 1)[:-1][0] + '/'
# id: get string after the last slash
id = fullpath.rsplit('/', 1)[-1]
print("ID:", id, "\tsubpath:", subpath)
# try to send the file if the subpath is valid
if subpath in pathFits1:
print("valid path, attempting to sending file")
try:
return send_file(subpath + id, as_attachment=True, attachment_filename=id)
except Exception as e:
print(e)
return "file not found"
return "invalid path"
However, a much better implementation would be to send the file ID and path as GET request parameters (eg. http://127.0.0.1:8000/returnimg?id=3&path=content/images1/) and retrieve them like this:
from flask import Flask, send_file, request
app = Flask(__name__)
pathFits1 = [None] * 3
pathFits1[1] = 'content/images1/'
pathFits1[2] = 'content/images2/'
#app.route('/returnimg', methods=['GET'])
def returnImg():
global pathFits1
id = request.args.get('id')
path = request.args.get('path')
print("ID:", id, "\tpath:", path)
# try to send the file if the subpath is valid
if path in pathFits1:
print("valid path, attempting to sending file")
try:
return send_file(path + id, as_attachment=True, attachment_filename=id)
except Exception as e:
print(e)
return "file not found"
return "invalid path"
I am generating a xlsx file which I would like to download once it is created. The file is created using a module called 'xlsxwriter'. It saves the file in my root directory, however I cant figure out how to access it via flask, so that it starts a download.
This is how I create the file:
workbook = xlsxwriter.Workbook('images.xlsx')
worksheet = workbook.add_worksheet()
worksheet.write(..someData..)
It saves the file in my root directory.
Now I am trying to access it in order to download it via flask:
app = Flask(__name__, static_url_path='')
#app.route('/download')
def download():
# do some stuff
return Response(
app.send_static_file('images.xlsx'),
mimetype="xlsx",
headers={"Content-disposition":
"attachment; filename=images.xlsx"})
However, I get a 404 Error. Is using send_static_file the correct way to go here?
I found a solution using 'send_file' instead. Providing the path to my file like so:
from flask import send_file
return send_file(pathToMyFile, as_attachment=True)
So I am trying to test out serving some user uploaded files in Flask. For images I am simply renaming them with a shortened UUID and putting them in a folder, but for other file types I would like to retain the original filename, so I devised the convoluted method of saving each file in a subfolder named with a UUID. Everything works fine, both the images and files upload and are in the directories they should be in. Just to test I made a template for a download page to just display the filename(planned to implement a download button later). However, when I plug the generated URL in for an uploaded file, I get a 404, and the function that url is supposed to be bound to doesn't even appear to execute(I had it print the filename in console and it doesnt even print), and in console the url is displayed: "GET /xVgePgj2Y HTTP/1.1" 404 -
My code for uploading the files and making the URL:
else:
new_folder_name = shortuuid.uuid()[:9]
os.mkdir(os.path.join(app.config['FILE_FOLDER'], new_folder_name))
file.save(os.path.join(os.path.join(app.config['FILE_FOLDER'], new_folder_name), filename))
new_folder_path = os.path.join(app.config['FILE_FOLDER'], new_folder_name)
return url_for('uploaded_file', new_folder_name=new_folder_name)
My code for serving the files:
#app.route('/<new_folder_name>', methods=['GET'])
def uploaded_file(new_folder_name):
filename = subfolder_fetch(new_folder_name)
return render_template("download.html", filename=filename)
and finally my code for fetching the filename from the subdirectory (called in the serving function - didn't pass the filename to the url_for function because that would make it ugly and complicated):
def subfolder_fetch(new_folder_name):
stuff = os.listdir(os.path.join(app.config['FILE_FOLDER'], new_folder_name))
for name in folder:
print (name)
return name
I'm puzzled as to what is even going on and why my uploaded_file function isn't even being called.
Thanks in advance.