# HG changeset patch # User Pierre-Yves David # Date 1271078468 -7200 # Node ID aebd00a2d31674b1b0ae9cd6bf3b2e6227c04688 # Parent 08e7fa906cdb46e367f7042e26641390c6a011d0 [fix] fix path unicity process in BytesFileSystemStorage.new_fs_path The previous implementation was bugged (prefixing the whole path with '_' instead of the base name). A new version (using number) replace it. * * * Improve BytesFileSystemStorage.new_fs_path to use available metadata This version try to get some hint about how to name the file using metadata. Using the real file name and extension when available. Keeping the extension might be usefull for exemple in the case of processing that use filename extension to detect content-type. diff -r 08e7fa906cdb -r aebd00a2d316 server/sources/storages.py --- a/server/sources/storages.py Mon Apr 12 15:15:00 2010 +0200 +++ b/server/sources/storages.py Mon Apr 12 15:21:08 2010 +0200 @@ -1,6 +1,8 @@ """custom storages for the system source""" from os import unlink, path as osp +from yams.schema import role_name + from cubicweb import Binary from cubicweb.server.hook import Operation @@ -54,6 +56,22 @@ # * better file path attribution # * handle backup/restore +def uniquify_path(dirpath, basename): + """return a unique file name for `basename` in `dirpath`, or None + if all attemps failed. + + XXX subject to race condition. + """ + path = osp.join(dirpath, basename) + if not osp.isfile(path): + return path + base, ext = osp.splitext(path) + for i in xrange(1, 256): + path = '%s%s%s' % (base, i, ext) + if not osp.isfile(path): + return path + return None + class BytesFileSystemStorage(Storage): """store Bytes attribute value on the file system""" def __init__(self, defaultdir): @@ -102,16 +120,27 @@ DeleteFileOp(entity._cw, filepath=self.current_fs_path(entity, attr)) def new_fs_path(self, entity, attr): - fspath = osp.join(self.default_directory, '%s_%s' % (entity.eid, attr)) - while osp.exists(fspath): - fspath = '_' + fspath + basename = [str(entity.eid), attr] + # We try to get some hint about how to name the file using attributes + # metadata. Using the real file name and extension when available. + # + # Keeping the extension might be usefull for exemple in the case of PIL + # processing that use filename extension to detect content-type. + name = entity.attr_metadata(attr, 'name') + if name is not None: + basename.append(name.encode(entity._cw.encoding)) + fspath = uniquify_path(self.default_directory, '_'.join(basename)) + if fspath is None: + msg = entity._cw._('failed to uniquify path (%s, %s)') % ( + dirpath, basename) + raise ValidationError(entity.eid, {role_name(attr, 'subject'): msg}) return fspath def current_fs_path(self, entity, attr): sysource = entity._cw.pool.source('system') cu = sysource.doexec(entity._cw, 'SELECT cw_%s FROM cw_%s WHERE cw_eid=%s' % ( - attr, entity.__regid__, entity.eid)) + attr, entity.__regid__, entity.eid)) rawvalue = cu.fetchone()[0] if rawvalue is None: # no previous value return self.new_fs_path(entity, attr)