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

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'

Related

django, upload tif image

I am trying to upload a tif image (greyscale), using django ImageField.
I have installed Pillow==8.3.1 and using Python 3.9.
The app works only with PNG/JPEG images.
Here is the model I am using:
class Upload(models.Model):
image = models.ImageField(upload_to='images')
title = models.CharField(max_length=200)
action = models.CharField(max_length=50,choices=ACTION_CHOICES)
updated = models.DateTimeField(auto_now=True)
created = models.DateTimeField(auto_now_add=True)
def __str__(self):
return self.title
#breakpoint()
# def __str__(self):
# pixels = tfi.imread(self.image)
# return np.shape(np.array(pixels))
def save(self,*args,**kwargs):
#open image
#breakpoint()
if self.action=='tif':
pixels = tfi.imread(self.image)
else:
pixels = Image.open(self.image)
#pixels = tfi.imread(self.image)
pixels = np.array(pixels)
pixels=pixels[:,:,0]
#pixels = pixels[0,:,:]
#use the normalisation method
img = get_image(pixels)
im_pil=Image.fromarray(img)
#save
buffer = BytesIO()
im_pil.save(buffer,format='png')
image_png = buffer.getvalue()
self.image.save(str(self.image), ContentFile(image_png),save=False)
super().save(*args,**kwargs)
#return self.image_png
Django's ImageField requires the third-party package Pillow. It depends on the pillow to verify that a file is indeed an image or not. This is not dependant on the file type, but on the content of the file itself.
please check your version of pillow you are using, and check the corresponding documentation of your pillow version.
https://pypi.org/project/Pillow/
supported image format by pillow 8.3.1
but if still something doesn't work for you you can always use FileField which doesn't look for what file or format u are uploading until u are uploading a file please check out documentation
https://www.geeksforgeeks.org/filefield-django-models/
Although I would recommend you to use imagefield for only image and read the documentation now it totally up to you what you want to use.

Django - Getting PIL Image save method to work with Amazon s3boto Storage

In order to resize images upon upload (using PIL), I'm overriding the save method for my Article model like so:
def save(self):
super(Article, self).save()
if self.image:
size = (160, 160)
image = Image.open(self.image)
image.thumbnail(size, Image.ANTIALIAS)
image.save(self.image.path)
This works locally but in production I get an error:
NotImplementedError: This backend doesn't support absolute paths.
I tried replacing the image.save line with
image.save(self.image.url)
but then I get an IOError:
[Errno 2] No such file or directory: 'https://my_bucket_name.s3.amazonaws.com/article/article_images/2.jpg'
That is the correct location of the image though. If I put that address in the browser, the image is there. I tried a number of other things but so far, no luck.
You should try and avoid saving to absolute paths; there is a File Storage API which abstracts these types of operations for you.
Looking at the PIL Documentation, it appears that the save() function supports passing a file-like object instead of a path.
I'm not in an environment where I can test this code, but I believe you would need to do something like this instead of your last line:
from django.core.files.storage import default_storage as storage
fh = storage.open(self.image.name, "w")
format = 'png' # You need to set the correct image format here
image.save(fh, format)
fh.close()
For me default.storage.write() did not work, image.save() did not work, this one worked. See this code if anyone is still interested. I apologize for the indentation. My project was using Cloudinary and Django small project.
from io import BytesIO
from django.core.files.base import ContentFile
from django.core.files.storage import default_storage as storage
def save(self, *args, **kargs):
super(User, self).save(*args, **kargs)
# After save, read the file
image_read = storage.open(self.profile_image.name, "r")
image = Image.open(image_read)
if image.height > 200 or image.width > 200:
size = 200, 200
# Create a buffer to hold the bytes
imageBuffer = BytesIO()
# Resize
image.thumbnail(size, Image.ANTIALIAS)
# Save the image as jpeg to the buffer
image.save(imageBuffer, image.format)
# Check whether it is resized
image.show()
# Save the modified image
user = User.objects.get(pk=self.pk)
user.profile_image.save(self.profile_image.name, ContentFile(imageBuffer.getvalue()))
image_read = storage.open(user.profile_image.name, "r")
image = Image.open(image_read)
image.show()
image_read.close()
If you are working with cloud storages for files in Django
NotImplementedError: This backend doesn't support absolute paths
To fix it you need to replace file.path with file.name
For code in the the question: image.save(self.image.path) with image.save(self.image.name)
Here how it looks like in the console
>>> c = ContactImport.objects.last()
>>> c.json_file.name
'protected/json_files/data_SbLN1MpVGetUiN_uodPnd9yE2prgeTVTYKZ.json'
>>> c.json_file
<FieldFile: protected/json_files/data_SbLN1MpVGetUiN_uodPnd9yE2prgeTVTYKZ.json>
>>> c.json_file.url
'https://storage.googleapis.com/super-secret/media/api/protected/json_files/data_SbLN1MpVGetUiN_uodPnd9yE2prgeTVTYKZ.json?Expires=1631378947&GoogleAccessId=secret&Signature=ga7...'

How can I programatically save an Image to an ImageField using Django-Cumulus?

I am using Django-Cumulus to store images to Rackspace's Cloudfiles platform.
I want to, dynamically, manipulate my images and save them as a new ImageField for my Model. For example, I have a Photo model with these ImageFields: image, thumb_256x256
In my Form's save() method, I am letting the user specify the cropping locations (using JCrop).
Anyways, I know how to grab the existing image file that the user uploaded. I also know how to apply manipulations with PIL. The problem I'm running into is creating a new Rackspace File and writing to it.
I keep getting the exception "NoSuchObject".
Here's some example code:
def save(self, commit=True):
""" Override the Save method to create a thumbnail of the image. """
m = super(PhotoUpdateForm, self).save(commit=False)
image = Image.open(m.image.file)
image.thumbnail((256,256), Image.ANTIALIAS)
thumb_io = CloudFilesStorageFile(storage=CLOUDFILES_STORAGE, name='foo/bar/test.jpg')
image.save(thumb_io.file, format='JPEG')
Also, once I get to this point -- what's the best way of setting this image to the model's other ImageField? (m.thumb_256x256 in my case)
Thanks in advanced!
Update: The name of the actual Cloudfiles Django app I'm using is "django-cumulus"
Here is a temporary solution. I'm having an issue with setting the new filename properly. It simply appends a _X to the filename. So for example, somefilename.jpg becomes somefilename_1.jpg whenever I save a new version.
This code is a bit ugly but does get the job done. It creates a cropped version of the image and will also generate a thumbnail if needed.
def save(self, commit=True):
""" Override the Save method to create a thumbnail of the image. """
m = super(PhotoUpdateForm, self).save(commit=False)
# Cropped Version
if set(('x1', 'x2', 'y1', 'y2')) <= set(self.cleaned_data):
box = int(self.cleaned_data['x1']), \
int(self.cleaned_data['y1']), \
int(self.cleaned_data['x2']), \
int(self.cleaned_data['y2'])
image = Image.open(m.image.file)
image = image.crop(box)
temp_file = NamedTemporaryFile(delete=True)
image.save(temp_file, format="JPEG")
m.image.save("image.jpg", File(temp_file))
cropped = True # Let's rebuild the thumbnail
# 256x256 Thumbnail
if not m.thumb_256x256 or cropped:
if not image:
image = Image.open(m.image.file)
image.thumbnail((256,256), Image.ANTIALIAS)
temp_file = NamedTemporaryFile(delete=True)
image.save(temp_file, format="JPEG")
m.thumb_256x256.save("thumbnail.jpg", File(temp_file))
if commit: m.save()
return m

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

Storing Images on App Engine using Django

I'm trying to upload and save a resized image in a db.BlobProperty field on Google App Engine using Django.
the relevant part of my view that process the request looks like this:
image = images.resize(request.POST.get('image'), 100, 100)
recipe.large_image = db.Blob(image)
recipe.put()
Which seems like it would be the logical django equivalent of the example in the docs:
from google.appengine.api import images
class Guestbook(webapp.RequestHandler):
def post(self):
greeting = Greeting()
if users.get_current_user():
greeting.author = users.get_current_user()
greeting.content = self.request.get("content")
avatar = images.resize(self.request.get("img"), 32, 32)
greeting.avatar = db.Blob(avatar)
greeting.put()
self.redirect('/')
(source: http://code.google.com/appengine/docs/python/images/usingimages.html#Transform)
But, I keep getting an error that says: NotImageError / Empty image data.
and refers to this line:
image = images.resize(request.POST.get('image'), 100, 100)
I'm having trouble getting to the image data. Seems like it's not being uploaded but I can't figure out why. My form has the enctype="multipart/form-data" and all that. I think something's wrong with how I'm referring to the image data. "request.POST.get('image')" but I can't figure out how else to reference it. Any ideas?
Thanks in advance.
After some guidance from "hcalves" I figured out the problem. First of all, the default version of Django that comes bundled with App Engine is version 0.96 and how the framework handles uploaded files has changed since then. However in order to maintain compatibility with older apps you have to explicitly tell App Engine to use Django 1.1 like this:
from google.appengine.dist import use_library
use_library('django', '1.1')
You can read more about that in the app engine docs.
Ok, so here's the solution:
from google.appengine.api import images
image = request.FILES['large_image'].read()
recipe.large_image = db.Blob(images.resize(image, 480))
recipe.put()
Then, to serve the dynamic images back again from the datastore, build a handler for images like this:
from django.http import HttpResponse, HttpResponseRedirect
def recipe_image(request,key_name):
recipe = Recipe.get_by_key_name(key_name)
if recipe.large_image:
image = recipe.large_image
else:
return HttpResponseRedirect("/static/image_not_found.png")
#build your response
response = HttpResponse(image)
# set the content type to png because that's what the Google images api
# stores modified images as by default
response['Content-Type'] = 'image/png'
# set some reasonable cache headers unless you want the image pulled on every request
response['Cache-Control'] = 'max-age=7200'
return response
You access uploaded data via request.FILES['field_name'].
http://docs.djangoproject.com/en/dev/topics/http/file-uploads/
Reading more about Google's Image API, seems to me you should be doing something like this:
from google.appengine.api import images
image = Image(request.FILES['image'].read())
image = image.resize(100, 100)
recipe.large_image = db.Blob(image)
recipe.put()
request.FILES['image'].read() should work because it's supposed to be a Django's UploadedFile instance.