Zope Dynamic Images HowTo |
![]() |
Created by fowlertrainer . Last modified 2004-04-23 12:04:15. |
How to I create images on-fly in my site ? |
Some cases you need to get the image dynamically on your site.This time the simple, but boring way is to you upload all of the images to zope site, and use them with random, or id selection mode. But sometimes this way is not passable. In my case not too late I have a Firebird Database with many-many images. So I don't want to use static conversion, because it is many time, and everlasting synchronization is needed to get same datas for same ids. This time I needed other way to do it...Not too late I find a solution: When the browser load a page that containing images, first load the page, and next (or asynchronous later) load the images from sources. I see in php that: <image src="/getimage.php?imgid=112">This code is working in this way:
2.) Load the images from the defined sources as another requests. Call the server for the page "getimage.php" 3.) The "getimage.php" is load the images from any sources, and return with the image datas. 4.) The browser is show the page with images, or if not get right (valid) image data, show a sign. Hmmm... If it is possible in php then possible in zope too:<image src="/getdynaimage?imgid=112"<... where the "getdynaimage" is zpt, or python script, or dtml, or any type of object that can load, and return the needed image(s). Let see a simple example:I create a python script named "getdynaimage". We need to set the Content Type to define the image type, and we just simply return the data. See that: request=container.REQUEST RESPONSE=request.RESPONSE RESPONSE.setHeader('Content-Type','image/jpeg') return context.anyimage.dataObject named "anyimage" is a jpeg type image in Root Folder. The field "data" is a public member of the Image Object, and that field containing the uncoded bytes of the image. Same like native python: def LoadImageData(): f=open("any.jpg","rb") data=f.read() f.close() return data Well, it is very simple - but we have a little problem...This example is statically handle the image. We need some dynamic code. There is many way to do that:
2.) Load the images from FileSystem. 3.) Load the images from any source. The first two solution is good for simple sites, but it needed duplicated code to get same datas in the page, and in the image handler routine. See that: # Main Page <html tal:define="data here.main_init()"> This a page with dynamic images:<br> Type the image id: <form method="get"> <input tal:attributes="type python:'edit';name python:'id';value python:data[0]"> <input type="submit" name="btn_go" value="Show the image"> </form> <div tal:condition="python:data[1]<>0"> <image tal:attributes="src python:'/getdynaimage?imgid=+python:data[0]'> </div> </html> # main_init() rq=context.REQUEST result=-1;data=None if rq_has_key('id'): try: result=int(rq[id]) except: result=-1 sql="select * from image_database where id="+str(id) zsql=context.imageloadermethod rows=zsql(sql=sql) # Find we an image ? if len(rows)<>0: data=1 return [result,data] # getdynaimage request=container.REQUEST RESPONSE=request.RESPONSE RESPONSE.setHeader('Content-Type','image/jpeg') sql="select * from image_database where id="+str(id) zsql=context.imageloadermethod rows=zsql(sql=sql) if len(rows)<>0: data=None else: data=rows[0]['JPG'].data return dataAs you see, it is need the duplicated code to examine the image existing, and for load the data. And as you see, the code is good for one source, but for more source is unuseable. So I see for common solution, to handle any sources.The main problem is that: how to I store (by page request) preloaded image to share the picture data for each request of this user ? The problem is the multithreading working mode. Zope is (every web servers are) multithreaded application(s). So if you want to access shared datas, you must use Locks/Mutexes or other things to handle the asynchronous readings, and writing are (avoiding the data corruption). But locks are not accessable under Zope, only with outer Products. So I'll trying with other solution. That is: an unique ID generator, and the SESSION.Every Zope requests are using the SESSION variable. That is unique for every users, and browser depending. But it is ideal place to we store the image body in it. Let we see, how to do it ! In first, we need to create a folder in root of the site named "/testdyna". Ok, we move in it. We must load an image into this folder, named "video". It is a jpeg type. When it is ok, we may write the codes. So we need an unique ID generator at first. We use random, and the time modules to generate id. ## Script (Python) "getuniqueid" ##bind container=container ##bind context=context ##bind namespace= ##bind script=script ##bind subpath=traverse_subpath ##parameters= ##title= ## dt=context.ZopeTime() s=dt.strftime('%Y%m%d%H%M%S')+str(dt.millis()) import random d=str(random.random()*random.randint(1,32768)) d=d.replace(".","0") d=d.replace("-","0") d=d.replace(":","0") return s+dNext we need a mechanism to store the image in SESSION. ## Script (Python) "setdynaimagedata" ##bind container=container ##bind context=context ##bind namespace= ##bind script=script ##bind subpath=traverse_subpath ##parameters=imgid,data ##title= ## rq=context.REQUEST ss=rq.SESSION s=str('dynaimagedatas_'+str(imgid)) # # this lines are needed, because the zope is # cannot store the buffer types ! # So we convert the buffer to string # ls="" for c in data: ls=ls+c ss[s]=lsOk, we can store the image, but how to we get it in the image loading request ? Very simply: with id. ## Script (Python) "returndynaimagedata" ##bind container=container ##bind context=context ##bind namespace= ##bind script=script ##bind subpath=traverse_subpath ##parameters= ##title= ## request = container.REQUEST RESPONSE = request.RESPONSE RESPONSE.setHeader('Content-Type','image/jpeg') imgid='0' if request.has_key('imgid'): imgid=request['imgid'] s=str('dynaimagedatas_'+str(imgid)) return request.SESSION[s]For hiding this method from out we create an alias: ## Script (Python) "getdynaimage" ##bind container=container ##bind context=context ##bind namespace= ##bind script=script ##bind subpath=traverse_subpath ##parameters= ##title= ## return context.returndynaimagedata()Ok, we can store, can get the image data. But how to create the page ? I split this work to 2 object: a zpt, and a pyscript. Let see them. The init script: ## Script (Python) "init" ##bind container=container ##bind context=context ##bind namespace= ##bind script=script ##bind subpath=traverse_subpath ##parameters= ##title= ## # We can change this to load the image from any source ! jpegdata=context.video.data imgid="pic_"+context.getuniqueid() context.setdynaimagedata(imgid,jpegdata) ids=[] ids.append(imgid) imgid="pic_"+context.getuniqueid() context.setdynaimagedata(imgid,jpegdata) ids.append(imgid) return idsThe page source: <html> <head> <title tal:content="template/title">The title</title> </head> <body> <h2><span tal:replace="here/title_or_id">content title or id</span> <span tal:condition="template/title" tal:replace="template/title">optional template id</span></h2> This is Page Template <em tal:content="template/id">template id</em>. <div tal:define="data python:here.init()"> <p tal:replace="python:str(data)"/> <br>Here we show the images:<br> <img tal:attributes="src python:'/testdyna/getdynaimage?imgid='+data[0];border python:0"/> <img tal:attributes="src python:'/testdyna/getdynaimage?imgid='+data[1];border python:0"/> </div> </body> </html> That's all. Why it is better than the 1.) and 2.) examples ?Because the main routines are useable for any time, and there is not need to duplicate the image loaders. When it is not better than the 1.) and 2.) examples ?When the page images are requested by the browser later than 1 hour, the images are maybe unaccessable, because if the SESSION is time limited, Zope is every N minutes drop it's SESSION content - with the images. (Commonly this cases are happening, when the user is load only the image, or load the page, but many minutes later is want to refresh, or reload the items without the main page.) To handle this problem, you need a Transient Object Container with time limit 3660, or more minutes. It is growing your site's "Data.fs", so if you want to use it, please calculate with this effect. There is the XML export file of this folder, and the objects. Use my email address for questions, comments, bug reportings. |