Ignore:
Timestamp:
16 Nov 2011, 18:05:03 (13 years ago)
Author:
uli
Message:

Make imagestorage sensible for different filename extensions per upload doc.

Location:
main/waeup.sirp/trunk/src/waeup/sirp
Files:
2 edited

Legend:

Unmodified
Added
Removed
  • main/waeup.sirp/trunk/src/waeup/sirp/imagestorage.py

    r7105 r7120  
    2020## Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
    2121##
    22 """A storage for image files.
     22"""A storage for image (and other) files.
    2323
    2424A few words about storing files with ``waeup.sirp``. The need for this
     
    8484Later, we can get the file back like this:
    8585
    86   >>> store.getFile('myfile.txt')
     86  >>> store.getFile('myfile')
    8787  <open file ...>
     88
     89Please note, that we ask for ``myfile`` instead of ``myfile.jpg`` as
     90the file id should not make a difference for different filename
     91extensions. The file id for ``sample.jpg`` thus could simply be
     92``sample``.
    8893
    8994What we get back is a file or file-like object already opened for
    9095reading:
    9196
    92   >>> store.getFile('myfile.txt').read()
     97  >>> store.getFile('myfile').read()
    9398  'some file content'
    9499
     
    106111this::
    107112
    108   __<MARKER-STRING>__real-filename.jpg
     113  __<MARKER-STRING>__real-filename
    109114
    110115Please note the double underscores before and after the marker
     
    128133case. For example if the file id would be
    129134
    130   ``__IMG_USER__manfred.jpg``
     135  ``__IMG_USER__manfred``
    131136
    132137then the looked up utility should be registered under name
     
    137142such utility can be found, a default handler is used instead
    138143(see :class:`DefaultFileStoreHandler`).
     144
     145**About File IDs and Filenames**
     146
     147In the waeup.sirp package we want to store documents like CVs,
     148photographs, and similar. Each of this documents might come into the
     149system with different filename extensions. This could be a problem as
     150the browser components might have to set different response headers
     151for different filetypes and we nevertheless want to make sure that
     152only one file is stored per document. For instance we don't want
     153``passport.jpg`` *and* ``passport.png`` but only one of them.
     154
     155The default components like :class:`DefaultFileStoreHandler` take care
     156of this by searching the filesystem for already existing files with
     157same file id and eventually removing them.
     158
     159Therefore file ids should never include filename extensions (except if
     160you only support exactly one filename extension for a certain
     161document). The only part where you should add an extension (and it is
     162important to do so) is when creating new files: when a file was
     163uploaded you can pass in the filename (including the filename
     164extension) and the file stored in external file store will (most
     165probably) have a different name but the same extension as the original
     166file.
     167
     168When looking for the file, you however only have to give the file id
     169and the handlers should find the right file for you, regardless of the
     170filename extension it has.
    139171
    140172**Context Adapters: Knowing Your Family**
     
    148180filename. You then would create filenames of a format like this::
    149181
    150   __<MARKER-STRING>__applicant0001.jpg
     182  __<MARKER-STRING>__applicant0001
    151183
    152184where ``applicant0001`` would tell exactly which applicant you can see
     
    182214
    183215"""
     216import glob
    184217import grok
    185218import os
     
    401434        """Store a file.
    402435        """
    403         file_id = filename
    404436        root = self.root # Calls to self.root are expensive
    405         marker, filename, base, ext = self.extractMarker(file_id)
     437        file_id = os.path.splitext(filename)[0]
     438        marker, filename, base, ext = self.extractMarker(filename)
    406439        handler = queryUtility(IFileStoreHandler, name=marker,
    407440                               default=DefaultFileStoreHandler())
    408441        f, path, file_obj = handler.createFile(
    409             self, root, file_id, filename, f)
     442            self, root, filename, file_id, f)
    410443        dirname = os.path.dirname(path)
    411444        if not os.path.exists(dirname):
     
    480513    grok.implements(IFileStoreHandler)
    481514
     515    def _searchInPath(self, path):
     516        """Get complete path of any existing file starting with `path`.
     517
     518        If no such file can be found, return input path.
     519
     520        If multiple such files exist, return the first one.
     521
     522        **Example:**
     523
     524        Looking for a `path`::
     525
     526          '/tmp/myfile'
     527
     528        will find any file like ``'/tmp/myfile.txt'``,
     529        ``'/tmp/myfile.jpg'`` and so on, if it exists.
     530        """
     531        result = path
     532        if os.path.isdir(os.path.dirname(path)):
     533            file_iter = glob.iglob('%s*' % (path,))
     534            try:
     535                result = file_iter.next()
     536            except StopIteration:
     537                pass
     538        return result
     539
    482540    def pathFromFileID(self, store, root, file_id):
    483         """Return the root path of external file store appended by file id.
    484         """
    485         return os.path.join(root, file_id)
     541        """Return a path for getting/storing a file with given file id.
     542
     543        If there is already a file stored for the given file id, the
     544        path to this file is returned.
     545
     546        If no such file exists yet (or the the only file existing has
     547        no filename extension at all) a path to store the file but
     548        without any filename extension is returned.
     549        """
     550        path = os.path.join(root, file_id)
     551        return self._searchInPath(path)
    486552
    487553    def createFile(self, store, root, filename, file_id, f):
     
    503569        file itself, it should leave that task to the calling file
    504570        store.
    505         """
     571
     572        This method does, however, remove any existing files stored
     573        under the given file id.
     574        """
     575        ext = os.path.splitext(filename)[1]
    506576        path = self.pathFromFileID(store, root, file_id)
    507         return f, path, HurryFile(filename, file_id)
     577        base, old_ext = os.path.splitext(path)
     578        if old_ext != ext:
     579            if os.path.exists(path):
     580                os.unlink(path)
     581            path = base + ext
     582        return f, path, HurryFile(filename, file_id + ext)
  • main/waeup.sirp/trunk/src/waeup/sirp/tests/test_imagestorage.py

    r7105 r7120  
    104104        return
    105105
     106    def test_create_file_w_ext(self):
     107        # We can store files with filename extension
     108        storage = ExtFileStore(root=self.workdir)
     109        dummy_file = StringIO('sample file')
     110        image_file = storage.createFile('mysample.txt', dummy_file)
     111        self.assertTrue('mysample.txt' in os.listdir(storage.root))
     112        self.assertEqual('mysample.txt', image_file.data)
     113        return
     114
    106115    def test_get_file(self):
    107116        # We can get files after having them stored
     
    111120        result = storage.getFile(image_file.data)
    112121        self.assertEqual(result.read(), 'sample file')
     122        return
     123
     124    def test_get_file_w_ext(self):
     125        # We can get files with filename extension after having them
     126        # stored
     127        storage = ExtFileStore(root=self.workdir)
     128        dummy_file = StringIO('sample file')
     129        image_file = storage.createFile('mysample.txt', dummy_file)
     130        result = storage.getFile('mysample')
     131        self.assertEqual(result.read(), 'sample file')
     132        return
     133
     134    def test_replace_file_w_new_ext(self):
     135        # when we store a file with the same file_id but different
     136        # filename extension, the old file will be deleted
     137        storage = ExtFileStore(root=self.workdir)
     138        dummy_file = StringIO('sample_file')
     139        image_file = storage.createFile('mysample.jpg', dummy_file)
     140        file1_path = storage.getFile('mysample').name
     141        new_file = storage.createFile(
     142            'mysample.png', StringIO('new file'))
     143        file2 = storage.getFile('mysample')
     144        self.assertEqual(file2.name[-12:], 'mysample.png')
     145        self.assertEqual(file2.read(), 'new file')
     146        # the old file was deleted
     147        self.assertFalse(os.path.exists(file1_path))
    113148        return
    114149
     
    147182        return os.path.join(root, file_id[12:])
    148183
    149     def createFile(self, store, root, file_id, filename, f):
    150         path = self.pathFromFileID(store, root, file_id)
    151         return f, path, HurryFile(filename, file_id)
     184    def createFile(self, store, root, filename, file_id, f):
     185        ext = os.path.splitext(filename)[1]
     186        path = self.pathFromFileID(store, root, file_id) + ext
     187        return f, path, HurryFile(filename, file_id + ext)
    152188
    153189class CustomContext(object):
     
    162198        # the `attr` parameter, a simple string.
    163199        if attr=='img':
    164             return '__mymarker__mysample.jpg'
     200            return '__mymarker__mysample_img.jpg'
    165201        elif attr=='doc':
    166             return '__mymarker__mysample.doc'
     202            return '__mymarker__mysample_doc.doc'
    167203        return '__mymarker__mysample.txt'
    168204
     
    246282        return
    247283
     284    def test_customized_handler_create_file_w_ext(self):
     285        # when we create a file of img type, the filename ext is taken
     286        # from input file.
     287        fs = ExtFileStore()
     288        result = fs.createFile(
     289            '__MYMARKER__sample_img.png', StringIO('sample text'))
     290        self.assertEqual(result.data, '__MYMARKER__sample_img.png')
     291        self.assertTrue('sample_img.png' in os.listdir(fs.root))
     292        return
     293
    248294    def test_customized_handler_get_file(self):
    249295        # we consider registered filename handlers when asking for
     
    279325        # each file has a different file id
    280326        self.assertEqual(file_id1, '__mymarker__mysample.txt')
    281         self.assertEqual(file_id2, '__mymarker__mysample.jpg')
    282         self.assertEqual(file_id3, '__mymarker__mysample.doc')
     327        self.assertEqual(file_id2, '__mymarker__mysample_img.jpg')
     328        self.assertEqual(file_id3, '__mymarker__mysample_doc.doc')
    283329        # each file has different content
    284330        self.assertEqual(result1.read(), 'my sample 1')
Note: See TracChangeset for help on using the changeset viewer.