Storing Images on App Engine using Django - 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.

Related

Django Reportlab getKeepWithNext

I am trying to add an image to a PDF generated in Reportlab. I am trying to access the image from a Django field, specifying the full path of the file.
When I run the below code I get: "Exception Value: getKeepWithNext".
Any help as to what I am doing wrong would be greatly appreciated.
def holding_pdf(self, course_slug, holding_slug):
buffer = io.BytesIO()
holding = HoldingDetail.objects.get(identifier=holding_slug)
doc = SimpleDocTemplate(buffer,
rightMargin=72,
leftMargin=72,
topMargin=72,
bottomMargin=72,
pagesize=A4,
title=f"Why the {holding.name} is in the portfolio.pdf")
elements = []
styles = getSampleStyleSheet()
elements.append(Paragraph(str(holding.logo.path), styles['Normal']))
elements.append(Image(holding.logo.path))
print(holding.logo.path)
doc.build(elements)
buffer.seek(0)
return FileResponse(buffer, as_attachment=False, filename=f"Why the {holding.name} is in the portfolio.pdf")
I think that you're using an incorrect Image class. Verify the python imports in your file, and sure you use the Image flowable class provides by reportlab.
from reportlab.lib.pagesizes import A4
from reportlab.lib.styles import getSampleStyleSheet
from reportlab.platypus import Paragraph, SimpleDocTemplate, Image

Flask app -how to display an image generated from within the app?

so I have some python code that generates an image inside my Flask app (can do it in JPG,PNG,etc..) and I want to display that image on my Flask app. the problem is that it needs to live in the 'static' folder,
and it is 'read only' - so I can't create the image inside it.
can I make the 'url_for' look inside other directories? or somehow write my image file into the 'static' folder in runtime?
I had a similar issue on one of my pet projects. I'm not sure if there's a better way to do it but I managed to get around it by encoding the image in base64 and passing the image tag to the html file directly via render_template(). Essentially:
import io
def serve_pil_image(pil_img):
img_io = io.BytesIO()
pil_img.save(img_io, 'jpeg', quality=100)
img_io.seek(0)
img = base64.b64encode(img_io.getvalue()).decode('ascii')
img_tag = f'<img src="data:image/jpg;base64,{img}" class="img-fluid"/>'
return img_tag
And in your flask app:
from PIL import Image
#app.route('/')
def index():
my_image = Image.open(image_file)
img_tag=serve_pil_image(my_image)
return render_template('index.html', image=img_tag)
And in your html:
{{image|safe}}

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 admin upload and image to s3 and then resize the image and save a thumb problem

I am having error after error trying to upload and resize images to s3 with pil and botos3 and the django default_storage. I am trying to do this on save in the admin.
here is the code:
from django.db import models
from django.forms import CheckboxSelectMultiple
import tempfile
from django.conf import settings
from django.core.files.base import ContentFile
from django.core.files.storage import default_storage as s3_storage
from django.core.cache import cache
from datetime import datetime
import Image, os
import PIL.Image as PIL
import re, os, sys, urlparse
class screenshot(models.Model):
title = models.CharField(max_length=200)
slug = models.SlugField(max_length=200)
image = models.ImageField(upload_to='screenshots')
thumbnail = models.ImageField(upload_to='screenshots-thumbs', blank=True, null=True, editable=False)
def save(self):
super(screenshot, self).save() # Call the "real" save() method
if self.image:
thumb = Image.open(self.image.path)
thumb.thumbnail(100, 100)
filename = str(self.slug)
temp_image = open(os.path.join('tmp',filename), 'w')
thumb.save(temp_image, 'JPEG')
from django.core.files import File
thumb_data = open(os.path.join('/tmp',filename), 'r')
thumb_file = File(thumb_data)
new_file.thumb.save(str(self.slug) + '.jpg', thumb_file)
def __str__(self):
return self.title
This is just one of the many ways I have tried to get it working, and I either get (2, 'No such file or directory') or some other error.
Please can someone help me to get it working. I want it to use the django backend to get the image uploaded to be resized and saved as the thumbnail and then saved. Let me know if you need to know any information. I would be happy to use the django snippet - http://djangosnippets.org/snippets/224/ but I don't know what data to feed it. I get the same IOErrors and 'no such path/filename' even though the main image is uploading to s3 fine. I have also tried things like:
myimage = open(settings.MEDIA_URL + str(self.image))
myimage_io = StringIO.StringIO()
imageresize = myimage.resize((100,100), Image.ANTIALIAS)
imageresize.save('resize_100_100_aa.jpg', 'JPEG', quality=75)
It's been 3 days of looking now so I am starting to go spare! Thanks
I had a similar problem, but in my case using sorl-thumbnail was not an option. I found that I can open an Image directly from S3BotoStorage by passing in a file descriptor instead of a path.
So instead of
thumb = Image.open(self.image.path)
use
thumb = Image.open(s3_storage.open(self.image.name))
Then you can process and save the new file locally as you were doing before.
Why don't you try sorl-thumbnail. It has the exact same interface as the default ImageField django provides and it seems like it would be a lot nicer to work with than the roll-your-own support.
Storage support
Pluggable Engine support (PIL, pgmagick)
Pluggable Key Value Store support (redis, cached db)
Pluggable Backend support
Admin integration with possibility to delete
Dummy generation
Flexible, simple syntax, generates no html
ImageField for model that deletes thumbnails
CSS style cropping options
Margin calculation for vertical positioning

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