How to find absolute path of uploaded image - Django 1.11 - django

I have a django form in which the user can upload an image that will then be displayed on a page. After the form request is submitted, I'm trying to return an httpresponse to the user of the uploaded image using the following code:
image_data = open("/path/to/my/image.png", "rb").read()
return HttpResponse(image_data, content_type="image/png")
The issue is that I can't get the absolute path from image submitted in the form request. By doing the following, I can get the name of the image, but not the local path to it:
name = ""
for filename, file in request.FILES.iteritems():
name = request.FILES[filename]
imageFileName = name
I've tried using the function file_to_string() based on an SO post, but it looks like the function is deprecated now. How can I get the absolute file path so I can pass it to the open function to return the image?
models.py
class PhotoUploader(models.Model):
title = models.CharField(max_length =120)
image = models.ImageField()

Here the solutions:
once you save the image,then you may got the path like that:
instance = PhotoUploader.objects.get(id=instance_id);
image_full_path = instance.image.path
image_data = open(image_full_path, "rb").read()
return HttpResponse(image_data, content_type="image/png")
"image_full_path" this should be your uploaded image full path.

Related

Django generate file and save it to model

My Django app generates a file. It takes img1.png and watermark.png and past them together and saves it again in the folder.
Everything works as expected.
This is the function:
def generate():
img1 = Image.open(f'{current_path}/media/pdf/img1.png')
img2 = Image.open(f'{current_path}/media/watermark.png')
img1.paste(img2, (150, 250), img2)
img1.save(f'{current_path}/media/pdf/generatedfile.png')
When working locally on my computer everything is good with specifying the path. However, in production it does not work anymore. I need to save the generatedfile.png directly on AWS S3.
For this reason I have create a simple model:
class pngUploadModel(models.Model):
auto_increment_id = models.AutoField(primary_key=True, default=True)
image = models.ImageField(null=True, blank=True, upload_to="png/")
I am able to upload images to this model using the admin interface. Everything still works as expected.
Now to my struggle.
I need to generate the image, and saving it "directly" to the model. Without saving it first to the path (because this will not work in production).
Approach:
def generate():
img1 = Image.open(f'{current_path}/media/pdf/img1.png')
img2 = Image.open(f'{current_path}/media/watermark.png')
img1.paste(img2, (150, 250), img2)
img1.save(f'{current_path}/media/pdf/generatedfile.png')
try:
filename = pngUploadModel.objects.create()
filename.image = img2
print('worked!')
except Exception as e:
print(f'this is the error message: {e}')
Output:
It creates an object in my model which I can see on my admin panel, but the model is empty, there is no image.
How can I save the generated image to my model, without having to save it first to my local directory. I was thinking to use something like a tempfile but I do not know if it is the right way.
If I'm correct, you want to get the generated file from the file path (f'{current_path}/media/pdf/generatedfile.png') and save it to your pngUploadModel.
An approach that I remember taking recently was to use the same prefix of the generated filename, setting that to be where the model instance image field points. For example:
def generate():
img1 = Image.open(f'{current_path}/media/pdf/img1.png')
img2 = Image.open(f'{current_path}/media/watermark.png')
img1.paste(img2, (150, 250), img2)
img1.save(f'{current_path}/media/pdf/generatedfile.png')
# the prefix string of the generated file -> f'{current_path}/media/pdf/generatedfile.png'
try:
genFile = pngUploadModel()
# Using the path/prefix of the generated file to be set to the image field
genFile.image = f'{current_path}/media/pdf/generatedfile.png'
genFile.save()
print('worked!')
except Exception as e:
print(f'this is the error message: {e}')
I used this answer as my guide then and it worked perfectly.
Another way is to save the generated file to the image field by passing a few arguments to the save() on the image/file field. Example:
from django.core.files.base import ContentFile # ensure you import
def generate():
prefix = f'{current_path}/media/pdf/generatedfile.png'
img1 = Image.open(f'{current_path}/media/pdf/img1.png')
img2 = Image.open(f'{current_path}/media/watermark.png')
img1.paste(img2, (150, 250), img2)
img1.save(prefix)
# the prefix string of the generated file -> f'{current_path}/media/pdf/generatedfile.png'
# with open('/path/to/already/existing/file', 'rb') as f:
with open(prefix, 'rb') as f:
data = f.read()
genFile = pngUploadModel()
genFile.image.save('generatedfile.png', ContentFile(data))
genFile.save()
Ideally, that should work. You can also view other answers to this question as they might be helpful or can be used for future reference.

How can I serve an image in django?

I have a binary field to save images
photograph = models.BinaryField(default=None)
In my form, I save the image
photograph = cd['photograph'].file.getvalue(),
)
In My view
f = open('my.jpeg', 'bw')
myfile = File(f)
myfile.write(student.photograph)
filepath = os.path.abspath(os.path.realpath('my.jpeg'))
context['urls'] = filepath
return render(request, 'dashboard.html', context)
The image is saved to the database, it is being retrieved successfully.
Screenshot of the image being saved successfully
My template
The HTML in the template renders well.
If I copy the HTML into a local file, the image appears well and good.
However, the image doesn't load properly when I use django.
Right click > copy image address gives me this: about:blank#blocked
Is it a security or a permissions issue?
After much research, this is what I found.
in HTML
<img src = "data/image:jpeg;base64, {{base64_string}}/>
in view
from django.http import urlsafe_b64encode
return render(request, 'template.html', {'base64_string' : urlsafe_b64encode(myobject.photograph)
This works for development. For production, I guess static files could be served the django way.

how to not display original picture name in Django

I am building a Django project where users can upload pictures. I am wondering what I should do to not show the original picture name.
I want the url to be something like /pic/randomnumber, and when the picture is downloaded from the website, it would have the name randomnumber.jpg. For example, all the pictures on Tumblr have the name tumblr_blabla.jpg.
I think this is something that should be done in models.py, but I am not quite sure how to implement it.
IMO you should write method save in your model
Something like that:
from PIL import Image
import os
class YOURS_MODEL_NAME(models.Model):
photo = models.ImageField(upload_to="photos")
def save(self, miniature=True):
super(YOURS_MODEL_NAME, self).save()
if miniature:
filepath = self.photo.path
image = Image.open(filepath)
new_filepath = filepath.split('.')
new_filepath = '.'.join("HERE YOU CAN ADD EVERYTHING TO PATH TO THIS PHOTO") + "." + new_filepath[-1].lower()
try:
image.save(new_filepath, quality=90, optimize=1)
except:
image.save(new_filepath, quality=90)
photo_name = self.photo.name.split('.')
photo_name = '.'.join("HERE YOU CAN ADD EVERYTHING YOU WANT TO 'PHOTO NAME'") + "." + photo_name[-1].lower()
self.photo = photo_name
self.save(miniature=False)
# remove old image
os.remove(filepath)
The upload_to argument in your Model definition can be a callable function which you use to customize the name of the file. Taken from the Django docs on
FileField (of which ImageField is a subclass):
upload_to takes two arguments: instance and filename, (where filename is the original filename, which you may also chose to ignore).
Something similar to this in models.py should do the trick:
def random_filename(instance, filename):
file_name = "random_string" # use your choice for generating a random string!
return file_name
class SomeModel(models.Model):
file = models.ImageField(upload_to=random_filename)
(this is similar to the answer this question about FileFields).
If you are going down this path, I would recommend that you use either the hash/checksum or date/time of the file upload. Something along these lines should work (although I haven't tested it myself!):
from hashlib import sha1
def unique_filename(instance, field):
filehash = sha1()
for chunk in getattr(instance, field).chunks():
filehash.update(chunk)
return filehash
class SomeModel(models.Model):
file = models.ImageField(upload_to=unique_filename(field='file'))
Hope this helps!

Resize thumbnails django Heroku, 'backend doesn't support absolute paths'

I've got an app deployed on Heroku using Django, and so far it seems to be working but I'm having a problem uploading new thumbnails. I have installed Pillow to allow me to resize images when they're uploaded and save the resized thumbnail, not the original image. However, every time I upload, I get the following error: "This backend doesn't support absolute paths." When I reload the page, the new image is there, but it is not resized. I am using Amazon AWS to store the images.
I'm suspecting it has something to do with my models.py. Here is my resize code:
class Projects(models.Model):
project_thumbnail = models.FileField(upload_to=get_upload_file_name, null=True, blank=True)
def __unicode__(self):
return self.project_name
def save(self):
if not self.id and not self.project_description:
return
super(Projects, self).save()
if self.project_thumbnail:
image = Image.open(self.project_thumbnail)
(width, height) = image.size
image.thumbnail((200,200), Image.ANTIALIAS)
image.save(self.project_thumbnail.path)
Is there something that I'm missing? Do I need to tell it something else?
Working with Heroku and AWS, you just need to change the method of FileField/ImageField 'path' to 'name'. So in your case it would be:
image.save(self.project_thumbnail.name)
instead of
image.save(self.project_thumbnail.path)
I found the answer with the help of others googling as well, since my searches didn't pull the answers I wanted. It was a problem with Pillow and how it uses absolute paths to save, so I switched to using the storages module as a temp save space and it's working now. Here's the code:
from django.core.files.storage import default_storage as storage
...
def save(self):
if not self.id and not self.project_description:
return
super(Projects, self).save()
if self.project_thumbnail:
size = 200, 200
image = Image.open(self.project_thumbnail)
image.thumbnail(size, Image.ANTIALIAS)
fh = storage.open(self.project_thumbnail.name, "w")
format = 'png' # You need to set the correct image format here
image.save(fh, format)
fh.close()
NotImplementedError: This backend doesn't support absolute paths - can be fixed by replacing file.path with file.name
How it looks in the the console
c = ContactImport.objects.last()
>>> c.json_file
<FieldFile: protected/json_files/data_SbLN1MpVGetUiN_uodPnd9yE2prgeTVTYKZ.json>
>>> c.json_file.name
'protected/json_files/data_SbLN1MpVGetUiN_uodPnd9yE2prgeTVTYKZ.json'

Django - Saving thumbnail with different filename

I want to create thumbnails of uploaded image files and save them with "_th" at the end of the filename. Currently, I am using the following code:
def _create_thumbnail(img_path):
image = Image.open(img_path)
if image.mode not in ("L", "RGB"):
image = image.convert("RGB")
image.thumbnail(MEDIA_THUMBNAIL_SIZES, Image.ANTIALIAS)
return image.save(img_path, 'JPEG', quality=MEDIA_THUMBNAIL_QUALITY)
It overwrites the original file. Is there a way to easily change the name of the file to include _th before the file extension and save it in the same place?
Also, I am saving the thumbnail after the post save signal using the following method:
#receiver(post_save, sender=Media, dispatch_uid="media_create_thumb")
def create_media_thumbnail(sender, **kwargs):
thumb = generate_thumbnail(kwargs['instance'].file)
I was wondering if this is an ok (pythonic?) way of using signals? Since I am not using the django admin panel, using the admins post save isn't an option.
This method to create thumbnails will be open to users, so if there is anything about the above code which might cause problems, I'd appreciate the heads up!
I would try the following:
import os
(head, tail) = os.path.split(img_path)
(name,ext)=tail.split('.')
tail=name+'_th.'+ext
img_path=os.path.join(head,tail)
edit:
as i found out recently, you can even shortcut that:
(name,ext)=os.path.splitext(img_path)
img_path = name + '_th.' + ext