step to save image file to server using django api - python

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.

Related

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)

Cannot open Image file got from request.files.get()

I am trying to make a face recognition API with Flask and deepface library.
But I am not able to open the image it's giving me different errors. Please give me a solution.
Code:
#app.route('/detect', methods=['POST'])
def recognize():
image_path = request.files.get('image')
try:
analysis = DeepFace.analyze(image_path)
return jsonify(analysis)
except Exception as e:
return jsonify({'error': str(e)})
error
"error": "object of type 'FileStorage' has no len()"
Tried to open the image
with open(image_path, 'r') as image:
analysis = DeepFace.analyze(image)
return jsonify(analysis)
I get the error
{"errors": "expected str, bytes or os.PathLike object, not FileStorage"}
I think image_path holds object of type FileStorage which knows location of a file and does not hold actual image data. First you need to load that image file and then try to analyze it.
A werkzeug FileStorage object has several methods as I've documented in another answer.
Sounds like you need to save the image to a temporary location first, then do the analysis.
I would create a directory called uploads on your server, then set this in the app config:
app.config['UPLOAD_FOLDER'] = 'uploads'
Then in your route, use the uuid.uuid4 function to create a temporary filename. Something like:
from uuid import uuid4
#app.route('/detect', methods=['POST'])
def recognize():
tmp_fname = os.path.join(app.config['UPLOAD_FOLDER'], uuid4().__str__())
image = request.files['image']
image.save(tmp_fname)
try:
analysis = DeepFace.analyze(tmp_fname)
return jsonify(analysis)
except Exception as e:
return jsonify({'error': str(e)})
This leaves the saved images on the filesystem, so you may wish to do some cleanup on this directory, or do something like os.remove('tmp_fname') after running the analysis.
I'm not sure whether Deepface.analyze accepts a stream as its first argument. The README suggests it only accepts a filename as a string. You could try doing Deepface.analyze(image.stream) to avoid having to deal with saving the temporary file (keep everything in memory instead), but this may not be supported.

Django/Dropbox uploaded file is 0 bytes

I CAN'T figure out why my uploaded file to Dropbox via Django is always zero bytes.
Outsize Django (using raw Python), the files get uploaded normally.
My HTML has enctype="multipart/form-data" declared.
VIEWS.PY:
if cv_form.is_valid():
cv_form.save(commit=True)
# print(request.FILES['cv'].size) == 125898
DOC = request.FILES['cv']
PATH = f'/CV/{DOC}'
upload_handler(DOC, PATH)
#http_response_happens_here()
HANDLER:
def upload_handler(DOC, PATH):
dbx = dropbox.Dropbox(settings.DROPBOX_APP_ACCESS_TOKEN)
dbx.files_upload(DOC.file.read(), PATH)
#file gets uploaded but always 0bytes in size
My bad! How did I not see it.
The solution incase anyone else runs into the same issue. Battled it for a while myself.
if cv_form.is_valid():
cv_form.save(commit=False) # commit should be False before upload
# print(request.FILES['cv'].size) == 125898
DOC = request.FILES['cv']
PATH = f'/CV/{DOC}'
upload_handler(DOC, PATH)
cv_form.save(commit=True) # now you can save the file.
#http_response_happens_here()

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.

Flask to return image stored in database

My images are stored in a MongoDB, and I'd like to return them to the client, here is how the code is like:
#app.route("/images/<int:pid>.jpg")
def getImage(pid):
# get image binary from MongoDB, which is bson.Binary type
return image_binary
However, it seems that I can't return binary directly in Flask? My idea so far:
Return the base64 of the image binary. The problem is that IE<8 doesn't support this.
Create a temporary file then return it with send_file.
Are there better solutions?
Create a response object with the data and then set the content type header. Set the content disposition header to attachment if you want the browser to save the file instead of displaying it.
#app.route('/images/<int:pid>.jpg')
def get_image(pid):
image_binary = read_image(pid)
response = make_response(image_binary)
response.headers.set('Content-Type', 'image/jpeg')
response.headers.set(
'Content-Disposition', 'attachment', filename='%s.jpg' % pid)
return response
Relevant: werkzeug.Headers and flask.Response
You can pass a file-like object and the header arguments to send_file to let it set up the complete response. Use io.BytesIO for binary data:
return send_file(
io.BytesIO(image_binary),
mimetype='image/jpeg',
as_attachment=True,
download_name='%s.jpg' % pid)
Prior to Flask 2.0, download_name was called attachment_filename.
Just wanted to confirm that dav1d's second suggestion is correct - I tested this (where obj.logo is a mongoengine ImageField), works fine for me:
import io
from flask import current_app as app
from flask import send_file
from myproject import Obj
#app.route('/logo.png')
def logo():
"""Serves the logo image."""
obj = Obj.objects.get(title='Logo')
return send_file(
io.BytesIO(obj.logo.read()),
download_name='logo.png',
mimetype='image/png'
)
Easier than manually creating a Response object and settings its headers.
Prior to Flask 2.0, download_name was called attachment_filename.
Suppose i have the stored image path with me. The below code helps to send image through.
from flask import send_file
#app.route('/get_image')
def get_image():
filename = 'uploads\\123.jpg'
return send_file(filename, mimetype='image/jpg')
uploads is my folder name where my image with 123.jpg is present.
[PS: The uploads folder should be in the current directory as of the your script file]
Hope it helps.
The following worked for me (for Python 3.7.3):
import io
import base64
# import flask
from PIL import Image
def get_encoded_img(image_path):
img = Image.open(image_path, mode='r')
img_byte_arr = io.BytesIO()
img.save(img_byte_arr, format='PNG')
my_encoded_img = base64.encodebytes(img_byte_arr.getvalue()).decode('ascii')
return my_encoded_img
...
# your api code
...
img_path = 'assets/test.png'
img = get_encoded_img(img_path)
# prepare the response: data
response_data = {"key1": value1, "key2": value2, "image": img}
# return flask.jsonify(response_data )

Categories