[Zope] Postgres and BLOBS

Yves Bastide Yves.Bastide@irisa.fr
Tue, 30 Apr 2002 15:00:23 +0200


On Saturday 27 April 2002 03:48, Colin Fox wrote:
 > Hi, everyone.
 >
 > I need to be able to store binaries (PDFs, JPEGs, etc) in postgres
 > tables and embed them in web pages.
 >
 > The idea is I have entries in the database and there is an image to go
 > with each entry.
 >
 > Postgres supports BLOBs, but there is a somewhat awkward interface to
 > using them (you have to actually extract the blob to a file on disk).
 >
 > I was wondering if anyone had created an object (simlar to the Zope
 > image object) that would let me reference binaries in the database
 > without having to do local file manipulation?


PostgreSQL supports two sorts of blobs: lo and bytea.
lo is the old, kludgy interface, whith a few advantages over bytea; most 
notably one can retrieve parts of a blob. And many well-known 
disadvantages.  Bytea corresponds to the (really minimal) standard SQL 
blob interface.

Micro-howto: Using a bytea (what follows is a quick slash-and-paste from 
an app here; may work or not as-is):

Say you've got a "File" table:

create table File (
	id serial primary key,
	fname text,
	mimetype text,
	content bytea);


Inserting a file
----------------

 From a form with a "file" input:

## Script (Python) "doInsFile"
##bind container=container
##bind context=context
##bind namespace=
##bind script=script
##bind subpath=traverse_subpath
##parameters=fichier
##title=
##
from psycopg import Binary

container.insertFile(
     fname = container.REQUEST.file.filename,
     mimetype = container.REQUEST.file.headers['Content-Type'],
     content = Binary(container.REQUEST.file.read())
     )


With the insertFile SQL method:

<params>fname:string
mimetype:string
content:string</params>
insert into file
  (fname, mimetype, content)
values (
  <dtml-sqlvar fname type="string">,
  <dtml-sqlvar mimetype type="string">,
  <dtml-var content>::bytea
)


For this to work, you must allow psycopg's Binary in a Product (see 
Products/PythonScripts/module_access_examples.py for instructions).


Retrieving a file
-----------------

The following query retrieves a file:

<params>id:int</params>
select
  fname,
  mimetype,
  octet_length(content) AS fsize,
  content
from file
where
  <dtml-sqltest id type="int">


It can be called by a Python script like:

files=container.qryFichier(id=id)
for file in files:
     RESPONSE.setHeader('Content-Type', file.mimetype)
     RESPONSE.setHeader('Content-Length', file.fsize)
     RESPONSE.setHeader('Content-Disposition', 'attachment; filename=%s'
                        % file.name)
     if file.fsize:
         RESPONSE.write(fic.content)


Misc. Remarks
-------------

* When uploading a file, IE gives the full pathname while NS and Moz 
return the filename with no path (I didn't try Konqueror, Opera and 
others). You should sanitize "file.filename" before putting it in the 
DB, if you do.

* Blob-access is totally DB-dependant.., even db-adapter-dependant. It's 
a PITA :-/


yves