Dynamic images with Image class - google-app-engine

I'm trying to do a simple case, output an image which is saved in a db.BlobProperty. I've read all of the GAE docs but can't seem to figure this one out.
Working: renders in browser
photo = Photo.get(photo_key)
self.response.headers['Content-Type'] = 'image/png'
self.response.write( photo.image )
Failing: what I really want to do is modify the image using images
from google.appengine.api import images
photo = Photo.get(photo_key)
img = images.Image(photo.image)
# no actual processing/transforms, ruling out that as an issue
self.response.headers['Content-Type'] = 'image/png'
self.response.write( img )
I believe it's a header issue. For some reason, when I write a Image object GAE is encoding it. The working example just returns with the image/png the failing give image/png; charset=utf-8.
I'm not sure what else to do since I am explicitly setting the Content-Type header in both cases. Thanks for the help.

You can't just use the images.Image object as the source for self.response.write. You need the actual data as a string, which you get from img.execute_transforms() even if you haven't asked for any transformations.

Related

Saved image cannot be opened using eligrey's filesaver

I am using react to create front-end.
I have a download button which will trigger an action.
The action will use axios.post to call the server which will return a file.
The axios.response is something like this
resopnse.data: 'binary data of image file'
response.headers: {
cache-control:"public, max-age=0"
content-disposition:"attachment; filename="test.jpg""
content-type:"image/jpeg"
last-modified:"Mon, 22 Jan 2018 18:49:27 GMT"
}
response.data is tested using postman which converts the response to the correct image.
Now I am going to use eligrey's filesaver to save it.
This is what I have.
let fileName = getFileNameFromContentDisposition(response.headers);
let blob = new Blob([response.data], {type: response.headers["content-type"]});
fileSaver.saveAs(blob, fileName, true);
The code is tested using Chrome. The code will create a jpeg file, but it cannot be opened.
I played around with solutions provided for similar questions in GitHub and this website. But none of it is working.
I believe I am missing trivial setting to make this work.
The problem is not the library.
axios is the cause. The response from axios is already converted to json. So the binary data lost some information.
Even when the blob string fromaxios's reply is converted back to blob. It is a corrupted blob.
The workaround is to use fetch, and convert the response to response.blob().

How can CakePHP return a Mimetype header of JPG?

Found this:
http://stackoverflow.com/questions/7198124/setting-the-header-to-be-content-image-in-cakephp
but either not understanding, or its not working for me.
Basically want to record 'opens' for emails. Currently it "works" but in gmail it shows that an image is not being displayed--so I want to return an actual image header. I've tried doing:
$this->layout=false;
$this->response->type('jpg');
In the Controller for Opens, but that is not working.
Web Sniffer (http://web-sniffer.net/), is showing a jpeg response, but still have a blank no file found image. How can I fix?
[edit]
Thinking this:
http://stackoverflow.com/questions/900207/return-a-php-page-as-an-image
and this:
http://book.cakephp.org/2.0/en/controllers/request-response.html
might be solution
Serve a real image
If you only send the headers for an image, but don't send the image content - it will be considered a broken image. To send a file refer to the documentation for whichever version of CakePHP you are using. For example in 2.3+:
public function opened() {
...
$this->response->file('/path/to/1x1.gif');
return $this->response;
}
Pretty sure this worked:
$name = './img/open.jpg';
$fp = fopen($name, 'rb');
$this->response->header("Content-Type: image/jpg");
$this->response->header("Content-Length: " . filesize($name));
fpassthru($fp);
Where open.jpg is a 1x1 real pixel image in cakephp's /img directory.
Would love if someone else could confirm?
Getting:
����JFIF``��C $.' ",#(7),01444'9=82<.342��C 2!!22222222222222222222222222222222222222222222222222��"����������?����
Going to manually (so I'm guessing thats a "real" image file?). No longer getting gmails no image file icon.
nvermind--websniff says this is a html request. will update when I figure out.
[edit]
think this might be correct way:
$this->response->type('jpg');
$this->response->file('./img/open.jpg');
just tested, and definitely getting a 1x1 pixel download. No jibberish like above.
Gmails caching proxy
Normally serving a real image as suggest by AD7Six would be the way to go, however with Gmails caching proxy in place you may run into problems when just serving an image.
http://www.emailmarketingtipps.de/2013/12/07/gmails-image-caching-affects-email-marketing-heal-opens-tracking/
The problem is/was that the image has been cached, and the proxy wouldn't request it a second time, making tracking of opens unreliable.
Content length to the rescue
Until recently the workaround for this has been to respond with a content length of 0 (and private cache control which seems to be necessary for some other webmail providers):
// ...
$this->response->header(array
(
'Cache-Control' => 'private',
'Content-Length' => 0
));
$this->response->type('gif');
return $this->response;
This would return a response with no content body, which is treated as broken, however not all clients did actually show a broken image symbol, still it was recommended to hide the image using styles on the img tag.
Return of cache control
However it has been reported that Gmail made some changes recently so that sending a no-cache header is now being respected again.
http://blog.movableink.com/real-time-content-and-re-open-tracking-return-to-gmail/
So in addition to AD7Six example an appropriate Cache-Control header might now do the trick
// ...
$this->response->header(array
(
'Cache-Control' => 'no-cache, max-age=0'
));
$this->response->file('/path/to/1x1.gif');
return $this->response;

Using bottle.py and blobstore GAE

I recently started using bottle and GAE blobstore and while I can upload the files to the blobstore I cannot seem to find a way to download them from the store.
I followed the examples from the documentation but was only successful on the uploading part. I cannot integrate the example in my app since I'm using a different framework from webapp/2.
How would I go about creating an upload handler and download handler so that I can get the key of the uploaded blob and store it in my data model and use it later in the download handler?
I tried using the BlobInfo.all() to create a query the blobstore but I'm not able to get the key name field value of the entity.
This is my first interaction with the blobstore so I wouldn't mind advice on a better approach to the problem.
For serving a blob I would recommend you to look at the source code of the BlobstoreDownloadHandler. It should be easy to port it to bottle, since there's nothing very specific about the framework.
Here is an example on how to use BlobInfo.all():
for info in blobstore.BlobInfo.all():
self.response.out.write('Name:%s Key: %s Size:%s Creation:%s ContentType:%s<br>' % (info.filename, info.key(), info.size, info.creation, info.content_type))
for downloads you only really need to generate a response that includes the header "X-AppEngine-BlobKey:[your blob_key]" along with everything else you need like a Content-Disposition header if desired. or if it's an image you should probably just use the high performance image serving api, generate a url and redirect to it.... done
for uploads, besides writing a handler for appengine to call once the upload is safely in blobstore (that's in the docs)
You need a way to find the blob info in the incoming request. I have no idea what the request looks like in bottle. The Blobstoreuploadhandler has a get_uploads method and there's really no reason it needs to be an instance method as far as I can tell. So here's an example generic implementation of it that expects a webob request. For bottle you would need to write something similar that is compatible with bottles request object.
def get_uploads(request, field_name=None):
"""Get uploads for this request.
Args:
field_name: Only select uploads that were sent as a specific field.
populate_post: Add the non blob fields to request.POST
Returns:
A list of BlobInfo records corresponding to each upload.
Empty list if there are no blob-info records for field_name.
stolen from the SDK since they only provide a way to get to this
crap through their crappy webapp framework
"""
if not getattr(request, "__uploads", None):
request.__uploads = {}
for key, value in request.params.items():
if isinstance(value, cgi.FieldStorage):
if 'blob-key' in value.type_options:
request.__uploads.setdefault(key, []).append(
blobstore.parse_blob_info(value))
if field_name:
try:
return list(request.__uploads[field_name])
except KeyError:
return []
else:
results = []
for uploads in request.__uploads.itervalues():
results += uploads
return results
For anyone looking for this answer in future, to do this you need bottle (d'oh!) and defnull's multipart module.
Since creating upload URLs is generally simple enough and as per GAE docs, I'll just cover the upload handler.
from bottle import request
from multipart import parse_options_header
from google.appengine.ext.blobstore import BlobInfo
def get_blob_info(field_name):
try:
field = request.files[field_name]
except KeyError:
# Maybe form isn't multipart or file wasn't uploaded, or some such error
return None
blob_data = parse_options_header(field.content_type)[1]
try:
return BlobInfo.get(blob_data['blob-key'])
except KeyError:
# Malformed request? Wrong field name?
return None
Sorry if there are any errors in the code, it's off the top of my head.

Get url of Image in GAE (Python 2.7)

I am trying to get URL of Image(blob field of GAE):
class Product(db.Model):
name = db.StringProperty()
price = db.FloatProperty()
added = db.DateTimeProperty(auto_now_add=True)
image = db.BlobProperty(default=None)
url = images.get_serving_url(movie.image)
Handler of serve image:
def result(request):
product = Product()
product.name = "halva"
url = 'http://echealthinsurance.com/wp-content/uploads/2009/11/minnesota.jpg'
product.image = db.Blob(urlfetch.Fetch(url).content)
product.put()
template = loader.get_template("result.html")
context = RequestContext(request,
{
"result" : u"Add"})
return HttpResponse(template.render(context))
But i get except:
UnicodeDecodeError:
When try to ignore this exception(that was bug in Python 2.7) I get exception in other place.
And after that i try to encode Image to 'latin-1'('utf-8' don't work):
enc_img = movie.image.decode("latin-1")
url = images.get_serving_url(enc_img)
Result: url has a view like binary file:
"ÝêÓ9>èýÑNëCf Äàr0xã³3Ï^µ7±\íQÀ¡>.....ÕÝ£°Ëÿ"I¢¶L`ù¥ºûMþÒ¸ÿ+ÿL¢ï£ÿÙ' alt="" />"
How I get url to show dynamic image in template?
You are confusing two different things here.
If you are storing your image in a db.BlobProperty (code doesn't show you are doing this, but the Schema you have is using db.BlobProperty) this means your handler has to serve the image.
However you are using image.get_serving_url, which takes a BlobKey instance which comes from storing an Image in the BlobStore https://developers.google.com/appengine/docs/python/blobstore/blobkeyclass which is a completely different thing to what you are doing.
You will need to work out what you want to do, store an image (max size 1MB) in a BlobProperty and provide a handler that can serve the image, or upload it to the BlobStore and serve it from there
images.get_serving_url takes a BlobKey. Try:
enc_img = movie.image
url = images.get_serving_url(enc_img.key())

Image serving from the high performance blobstore without direct access to get_serving_url()

I'm converting my site over to using the blobstore for image serving and am having a problem. I have a page with a large number of images being rendered dynamically (through jinja), and the only data available are entity keys that point to image objects that contain the relevant serving url.
Previously each image had a url along the lines of "/show-image?key={{image_key}}", which points to a request handler along the lines of this:
def get(self):
imageInfo = db.get(self.request.args.get("key"))
imagedata = imageInfo.data // the image is stored as a blob in the normal datastore
response = Response()
response.data = imagedata
response.headers['Content-Type'] = imageInfo.type
return response
My question is: How can I modify this so that, rather than returning a response with imageInfo.data, I return a response with imageInfo.saved_serving_url (generated from get_serving_url when the image object was created). More importantly, is this even a good idea? It seems like converting the saved_serving_url back into data (eg using urllib.fetch) might just counteract the speed and efficiency of using the high-speed datastore in the first place?
Maybe I should just rewrite my code so that the jinja template has direct access to the serving urls of each image. But ideally I'd like to avoid that due to the amount of parallel lists I'd have to pass about.
why not returning the serving url instead of the imagedata?
<img src="/show-image?key={{image_key}}" />
def get(self):
imageInfo = db.get(self.request.args.get("key"))
return imageInfo.saved_serving_url

Resources