Python Flask Restful After Request Delete File - python

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)

Related

step to save image file to server using django api

I am new in python and django, right now I would like to upload an image from postman (content type: form-data) and then save it in server. So far I have doing this
#csrf_exempt
def detector(request):
data = {"success": False}
if request.method == "POST":
print("oke0")
# check to see if an image was uploaded
if request.FILES.get("image", None) is not None:
...# here I would like to save the image
else:
return JsonResponse(data)
return JsonResponse(data)
so the flow is: upload image from postman and then directory 'media' will be created and the image will be stored there
so far I have been following this https://www.geeksforgeeks.org/python-uploading-images-in-django/ but I don't know how to try it in postman, anyone knows step by step to save image file to server in django?
Here is the code, but keep in mind that I didn't test it, since I wrote it on the fly.
I also assume you use Python3.2+, since you won't be able to use os.makedirs with exist_ok flag.
If you're using Python3.5+, you can also replace that code with something like Pathlib which can create folder, but won't raise an exception like so:
import pathlib
pathlib.Path('save_path').mkdir(parents=True, exist_ok=True)
and you can replace the os.makedirs in the code bellow with this call in that case.
import os
import uuid
#csrf_exempt
def detector(request):
data = {"success": False}
if request.method == "POST":
print("oke0")
if request.FILES.get("image", None) is not None:
#So this would be the logic
img = request.FILES["image"]
img_extension = os.path.splitext(img.name)[1]
# This will generate random folder for saving your image using UUID
save_path = "static/" + str(uuid.uuid4())
if not os.path.exists(save_path):
# This will ensure that the path is created properly and will raise exception if the directory already exists
os.makedirs(os.path.dirname(save_path), exist_ok=True)
# Create image save path with title
img_save_path = "%s/%s%s" % (save_path, "image", img_extension)
with open(img_save_path, "wb+") as f:
for chunk in img.chunks():
f.write(chunk)
data = {"success": True}
else:
return JsonResponse(data)
return JsonResponse(data)
Now, that's an easy part.
For Postman, simply follow this answer -> https://stackoverflow.com/a/49210637/1737811
Hope this answers your question.

How do I save a image using a flask API then return it to my React App can use it

I am trying to use my Flask API to save an image to the database OR just a file system but this is something I have never done and am getting nowhere with it.
I would like to be able to return the image back when the route is called and be able to use it in my ReactJS Application using just a img tag.
All I have been able to find is how to save the image to the Database and then download it using a route. I need to be able to return it. (It works just not what I need.)
Here is what that was:
#app.route('/img-upload', methods=['POST'])
def img_upload():
file = request.files['image']
newFile = Mealplan(name=file.filename, data=file.read())
db.session.add(newFile)
db.session.commit()
return jsonify({"Done!" : "The file has been uploaded."})
#app.route('/get-mealplan-image/<given_mealplan_id>')
def download_img(given_mealplan_id):
file_data = MealPlan.query.filter_by(id=given_mealplan_id).first()
return send_file(BytesIO(file_data.data), attachment_filename=file_data.name, as_attachment=True)
Save the files on the file system will be a more proper method. Here is a minimal example:
from flask import send_from_directory
basedir = os.path.abspath(os.path.dirname(__file__))
uploads_path = os.path.join(basedir, 'uploads') # assume you have created a uploads folder
#app.route('/img-upload', methods=['POST'])
def upload_image():
f = request.files['image']
f.save(os.path.join(uploads_path , f.filename)) # save the file into the uploads folder
newFile = Mealplan(name=f.filename) # only save the filename to database
db.session.add(newFile)
db.session.commit()
return jsonify({"Done!" : "The file has been uploaded."})
#app.route('/images/<path:filename>')
def serve_image(filename):
return send_from_directory(uploads_path, filename) # return the image
In your React app, you can use the filename to build to the image URL: /images/hello.jpg
Update:
If you can only get the id, the view function will be similar:
#app.route('/get-mealplan-image/<given_mealplan_id>')
def download_img(given_mealplan_id):
file_data = MealPlan.query.filter_by(id=given_mealplan_id).first()
return send_from_directory(uploads_path, file_data.name)

How to create dynamic file sending algorithm with flask

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"

In Django, how do I get a file path for an uploaded file when uploading?

I am trying to add some validation for user uploaded files. This requires running through a custom script I made called "sumpin", which only takes a filepath as a variable and sends back JSON data that will verify. Everything inside my script is working independently, putting it together where the error occurs.
Since this is file validation, I decided to expand my file_extension validator that was already working.
models.py
from allauthdemo.fileuploadapp.slic3rcheck import sumpin
def user_directory_path_files(instance, filename):
return os.path.join('uploads', str(instance.objectid), filename)
def validate_file_extension(value):
ext = os.path.splitext(value.name)[1]
valid_extensions = ['.stl','.STL']
if not ext in valid_extensions:
raise ValidationError(u'Please upload a .stl file type only')
data = sumpin(value.path)
print (data)
class subfiles(models.Model):
STL = models.FileField(_('STL Upload'),
upload_to=user_directory_path_files, validators=[validate_file_extension])
The error that I get is that the path (value.path) is not valid.
This is the incorrect path because the upload_to tag must change this at a later point. This may be obvious, but I also need to have the file at the filepath location when my script is called. So essentially my questions are...
How can pass the "upload_to" path into my validator to run through my custom script?
Is there a better method to deal with uploaded files, like in the main class with a "save" or "clean" function?
I've found my own answer, but I'll post it here in case someone runs across this issue in the future.
I was incorrect, a validator wouldn't actually download the file. I need to use a file upload handler, which is shown below.
import os
from django.core.files.storage import default_storage
from allauthdemo.fileuploadapp.slic3rcheck import sumpin
def handle_uploaded_file(f):
with open(default_storage.path('tmp/'+f.name), 'wb+') as destination:
for chunk in f.chunks():
destination.write(chunk)
data = sumpin(default_storage.path('tmp/'+f.name))
os.remove(default_storage.path('tmp/'+f.name))
return data
then I call this inside my views.py.
from allauthdemo.fileuploadapp.uploadhandler import handle_uploaded_file
#login_required
def STLupload(request):
# Handle file upload
if request.method == 'POST':
formA = ObjectUp(request.POST, request.FILES)
if formA is_valid():
data = handle_uploaded_file(request.FILES['STL'])
This will return whatever I called to return within handle_upload_file, which worked perfect for my issues. Hopefully someone will find this useful the future.

Sending a file via http in Python with bottle framework

How can I send a file as answer to an HTTP request in Python using the bottle framework?
For upload this code works for me:
#route('/upload', method='POST')
def do_upload():
category = request.forms.get('category')
upload = request.files.get('upload')
name, ext = os.path.splitext(upload.raw_filename)
if ext not in ('.png','.jpg','.jpeg'):
return 'File extension not allowed.'
save_path = category
upload.raw_filename = "hoho" + ext
upload.save(save_path) # appends upload.filename automatically
return 'OK'
Just call Bottle's static_file function and return its result:
#route('/static/<filename:path>')
def send_static(filename):
return static_file(filename, root='/path/to/static/files')
That example, when called with url /static/myfile.txt, will return the contents file /path/to/static/files/myfile.txt.
Other SO questions (like this one) contain additional examples.

Categories