source: main/waeup.sirp/trunk/src/waeup/sirp/imagestorage.py @ 6526

Last change on this file since 6526 was 6519, checked in by uli, 14 years ago

Add an image storage to store images for each site.

File size: 5.2 KB
Line 
1##
2## imagestorage.py
3## Login : <uli@pu.smp.net>
4## Started on  Mon Jul  4 16:02:14 2011 Uli Fouquet
5## $Id$
6##
7## Copyright (C) 2011 Uli Fouquet
8## This program is free software; you can redistribute it and/or modify
9## it under the terms of the GNU General Public License as published by
10## the Free Software Foundation; either version 2 of the License, or
11## (at your option) any later version.
12##
13## This program is distributed in the hope that it will be useful,
14## but WITHOUT ANY WARRANTY; without even the implied warranty of
15## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16## GNU General Public License for more details.
17##
18## You should have received a copy of the GNU General Public License
19## along with this program; if not, write to the Free Software
20## Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
21##
22"""A storage for image files.
23"""
24import grok
25import hashlib
26import os
27from StringIO import StringIO
28from ZODB.blob import Blob
29from persistent import Persistent
30from hurry.file.interfaces import IFileRetrieval
31from zope.container.contained import Contained
32from waeup.sirp.image import WAeUPImageFile
33from waeup.sirp.utils.helpers import cmp_files
34
35def md5digest(fd):
36    """Get an MD5 hexdigest for the file stored in `fd`.
37
38    `fd`
39      a file object open for reading.
40
41    """
42    return hashlib.md5(fd.read()).hexdigest()
43
44class Basket(grok.Container):
45    """A basket holds a set of image files with same hash.
46    """
47    def _del(self):
48        """Remove temporary files associated with local blobs.
49
50        A basket holds files as Blob objects. Unfortunately, if a
51        basket was not committed (put into ZODB), those blobs linger
52        around as real files in some temporary directory and won't be
53        removed.
54
55        This is a helper function to remove all those uncommitted
56        blobs that has to be called explicitly, for instance in tests.
57        """
58        key_list = self.keys()
59        for key in key_list:
60            item = self[key]
61            if getattr(item, '_p_oid', None):
62                # Don't mess around with blobs in ZODB
63                continue
64            fd = item.open('r')
65            name = getattr(fd, 'name', None)
66            fd.close()
67            if name is not None and os.path.exists(name):
68                os.unlink(name)
69            del self[key]
70        return
71
72    def getInternalId(self, fd):
73        for key, val in self.items():
74            fd.seek(0)
75            fd_stored = val.open('r')
76            if cmp_files(fd, fd_stored):
77                fd_stored.close()
78                return key
79            fd_stored.close()
80        return None
81
82    @property
83    def curr_id(self):
84        num = 1
85        while True:
86            if str(num) not in self.keys():
87                return str(num)
88            num += 1
89            if num <= 0:
90                name = getattr(self, '__name__', None)
91                raise ValueError('Basket full: %s' % name)
92
93    def storeFile(self, fd, filename):
94        internal_id = self.getInternalId(fd)
95        if internal_id is None:
96            internal_id = self.curr_id
97            self[internal_id] = Blob(fd.read())
98        return internal_id
99
100    def retrieveFile(self, basket_id):
101        if basket_id in self.keys():
102            return self[basket_id].open('r')
103        return None
104
105class ImageStorage(grok.Container):
106    """A container for image files.
107    """
108    def _del(self):
109        for basket in self.values():
110            try:
111                basket._del()
112            except:
113                pass
114
115    def storeFile(self, fd, filename):
116        fd.seek(0)
117        digest = md5digest(fd)
118        fd.seek(0)
119        if not digest in self.keys():
120            self[digest] = Basket()
121        basket_id = self[digest].storeFile(fd, filename)
122        full_id = "%s-%s" % (digest, basket_id)
123        return full_id
124
125    def retrieveFile(self, file_id):
126        if not '-' in file_id:
127            return None
128        full_id, basket_id = file_id.split('-', 1)
129        if not full_id in self.keys():
130            return None
131        return self[full_id].retrieveFile(basket_id)
132
133class ImageStorageFileRetrieval(Persistent):
134    grok.implements(IFileRetrieval)
135
136    def getImageStorage(self):
137        site = grok.getSite()
138        if site is None:
139            return None
140        return site.get('images', None)
141
142    def isImageStorageEnabled(self):
143        site = grok.getSite()
144        if site is None:
145            return False
146        if site.get('images', None) is None:
147            return False
148        return True
149
150    def getFile(self, data):
151        # ImageStorage is disabled, so give fall-back behaviour for
152        # testing without ImageStorage
153        if not self.isImageStorageEnabled():
154            return StringIO(data)
155        storage = self.getImageStorage()
156        if storage is None:
157            raise ValueError('Cannot find an image storage')
158        return storage.retrieveFile(data)
159
160    def createFile(self, filename, f):
161        if not self.isImageStorageEnabled():
162            return WAeUPImageFile(filename, f.read())
163        storage = self.getImageStorage()
164        if storage is None:
165            raise ValueError('Cannot find an image storage')
166        file_id = storage.storeFile(f, filename)
167        return WAeUPImageFile(filename, file_id)
Note: See TracBrowser for help on using the repository browser.