Nov 27

Blob support in the ZODB with ZEO

There's not a lot of documentation out there about ZODB blobs and how you configure a buildout with blob support in both ZEO and non-ZEO configurations for a pure Zope 3 application.

There's not a huge amount of information out there yet on the practicalities of configuring Zope (particularly Zope 3) to use blobs, and then actually using them in an application. I hope the following goes towards remedying that.

(I also hope the information I give below is correct; all I can say is that it works for me!)

Blobs without ZEO

The following buildout.cfg snippet will generate a Zope instance for you with blob support but without ZEO:

[buildout]
parts =
  zodb
  instance

[zodb]
recipe = zc.recipe.egg:script
eggs = ZODB3

[app]
recipe = zc.zope3recipes:app
base-site.zcml = <...snip site.zcml>

[instance]
recipe = zc.zope3recipes:instance
application = app
zope.conf =
    ${database:zconfig}
    devmode on
     <server>
       type HTTP
       address 127.0.0.1:8080
     </server>

[database]
recipe = zc.recipe.filestorage
blob-dir = ${buildout:parts-directory}/database/blobs

Blobs with ZEO

Same again, but with ZEO, reusing the [zodb] and [app] sections. Note that you have to specify blob directories for the ZEO clients, as well as the server. I suspect that clients require a local area to stick blobs to before they're streamed over to the ZEO server.

[zopectl]
recipe = zc.zope3recipes:instance
application = app
devmode = off
zope.conf =
   <zodb>
     <zeoclient>
       server 127.0.0.1:7100
     </zeoclient>
   </zodb>  

[instance1]
recipe = zc.zope3recipes:instance
extends = zopectl
address = 127.0.0.1:7080
zope.conf =
   <zodb>
     <zeoclient>
       server 127.0.0.1:7100
       cache-size 1000MB
       blob-dir /var/instances/blobs/instance1
     </zeoclient>
   </zodb>

[zeoserver]
recipe = zc.zodbrecipes:server
zeo.conf =
   <zeo>
      address 127.0.0.1:7100
   </zeo>
   <blobstorage 1>
    blob-dir /var/server/data/blobs
     <filestorage 1>
        path /var/server/data/Data.fs
     </filestorage>
   </blobstorage>

Blob support in the application


It then wasn't immediately obvious how to actually use blobs. In the end I started using z3c.blobfile. I defined a custom widget to wrap image file uploads in an instance of Image that supported blobs, and plugged that into a formlib custom_widget. The below snippet is actually collected from a number of files, but condensed here into one so you can see all the moving parts:


import zope
from zope.app.form.browser.textwidgets import FileWidget
from zope.formlib.form import FormBase
from zope.schema import Object
from z3c.blobfile.image import Image


class ImageWidget(FileWidget):
    def _toFieldValue(self, input):
        value = super(ImageWidget, self)._toFieldValue(input)
        return Image(value)


class MyForm(FormBase):
form_fields = Fields(IMyInterface)
    form_fields['image'].custom_widget = ImageWidget


class IMemberInfo(Interface):
    image = Object(
        title=_(u"schema-image-title"),
        description=_(u"schema-image-description"),
        required=False,
        schema=zope.app.file.interfaces.IImage
    )

I'm not yet sure how this affects maintenance, such as backups - whether a regular repozo backup also catches the blobs, or if they need to be backed up separately. I'm also not yet sure how the ZEO client blob directories work; hopefully they're just used as transient storage for a blob after they've been (say) uploaded from a web browser and attached to the ZODB locally, but before being streamed over to the ZEO server. There may or may not be housekeeping activities associated with these directories, too.

If you know the answers to any of those questions, please do post in the comments!

Anyway - all that represents about an evening's worth of research, so I hope it saves you some time.

I've disabled comments for now due to spam problems - I'll turn them back on when I've fixed it!

This won't be published anywhere, it's just in case I need to contact you.

You can use Markdown in your comments. Be sensible!

Sorry about this, but I don't want spam comments.