hurry.file是一个高级的Zope 3文件小部件,它尽可能地模仿其他小部件的行为,即使在表单由于验证错误而重新显示时也是如此。它还支持通过Tramline快速进行基于Apache的文件上传和下载。
项目描述
hurry.file字段
文件小部件是在HurryFile对象的基础上构建的
>>> from hurry.file import HurryFile >>> file = HurryFile('foo.txt', 'mydata') >>> file.filename 'foo.txt' >>> file.data 'mydata' >>> file.size 6 >>> f = file.file >>> f.read() 'mydata'
HurryFile对象在数据、文件名相匹配的情况下是相等的
>>> file1 = HurryFile('foo.txt', 'mydata') >>> file2 = HurryFile('foo.txt', 'mydata') >>> file3 = HurryFile('bar.txt', 'otherdata') >>> file1 == file2 True>>> file1 != file2 False>>> file1 == file3 False>>> file1 != file3 True
我们也可以从文件对象创建HurryFile对象
>>> from StringIO import StringIO >>> from zope import component >>> from hurry.file.interfaces import IFileRetrieval >>> fileretrieval = component.getUtility(IFileRetrieval) >>> file = fileretrieval.createFile('bar.txt', StringIO('test data')) >>> file.filename 'bar.txt' >>> file.size 9 >>> file.data 'test data' >>> f = file.file >>> f.read() 'test data'
这完全相同,但可能更容易使用
>>> from hurry.file import createHurryFile >>> file = createHurryFile('test2.txt', StringIO('another test file')) >>> file.filename 'test2.txt' >>> file.size 17
HurryFile对象通常使用ZODB持久性来存储文件数据。但是,文件也可以通过tramline存储。如果Apache中安装了tramline,则tramline负责生成文件ID并在文件系统中直接存储文件。然后,ID作为文件数据传递,以存储在ZODB中。
首先启用tramline。
tramline目录结构是一个包含两个子目录的目录,一个名为“repository”,另一个名为“upload”
>>> import tempfile, os >>> dirpath = tempfile.mkdtemp() >>> repositorypath = os.path.join(dirpath, 'repository') >>> uploadpath = os.path.join(dirpath, 'upload') >>> os.mkdir(repositorypath) >>> os.mkdir(uploadpath)
我们创建一个TramlineFileRetrieval对象,了解这个目录,并将其注册为一个实用工具
>>> from hurry.file.file import TramlineFileRetrievalBase >>> class TramlineFileRetrieval(TramlineFileRetrievalBase): ... def getTramlinePath(self): ... return dirpath >>> retrieval = TramlineFileRetrieval() >>> component.provideUtility(retrieval, IFileRetrieval)
现在让我们以 tramline 上传的方式存储文件
>>> f = open(os.path.join(repositorypath, '1'), 'wb') >>> f.write('test data') >>> f.close()
将创建一个名为“1”的文件(ZODB中存储的数据将是“1”)
>>> file = HurryFile('foo.txt', '1')
数据现在是“1”,指向实际文件
>>> file.data '1'
检索文件将返回实际文件
>>> f = file.file >>> f.read() 'test data'
我们还可以检索其大小
>>> file.size 9L
应该可以创建存储在目录结构中的 Hurry File 对象
>>> file = retrieval.createFile('test.txt', StringIO('my test data')) >>> file.filename 'test.txt'
现在我们为数据获取一个id
>>> file.data != 'my test data' True
并且我们可以检索文件本身
>>> f = file.file >>> f.read() 'my test data' >>> file.size 12L
现在让我们在我们的实用工具中禁用 tramline
>>> class TramlineFileRetrieval(TramlineFileRetrievalBase): ... def getTramlinePath(self): ... return dirpath ... def isTramlineEnabled(self): ... return False >>> component.provideUtility(TramlineFileRetrieval(), IFileRetrieval)
我们期望的行为与 tramline 未安装时相同
>>> file = HurryFile('foo.txt', 'data') >>> f = file.file >>> f.read() 'data' >>> file.size 4
清理
>>> import shutil >>> shutil.rmtree(dirpath)
hurry.file小部件
这是一个创建文件小部件的基础设施,该小部件尽可能地像 formlib 中的普通文本小部件一样表现。通常,由于表单验证失败的原因,文件小部件会在重新呈现表单时丢失其文件数据。一个
为了做到这一点,我们有一种特殊的方式来存储与文件名一起的文件数据
>>> from hurry.file import HurryFile >>> some_file = HurryFile('foo.txt', 'the contents') >>> some_file.filename 'foo.txt' >>> some_file.data 'the contents'
我们可以提供一个下载小部件。在这种情况下,没有可以下载的内容
>>> from hurry.file.browser import DownloadWidget >>> from hurry.file.schema import File >>> from zope.publisher.browser import TestRequest >>> field = File(__name__='foo', title=u'Foo') >>> field = field.bind(None) >>> request = TestRequest() >>> widget = DownloadWidget(field, request) >>> widget() u'<div>Download not available</div>'
即使请求中存在数据,也没有可以下载的内容
>>> from zope.publisher.browser import FileUpload >>> request = TestRequest(form={'field.foo': FileUpload(some_file)}) >>> widget = DownloadWidget(field, request) >>> widget() u'<div>Download not available</div>'
现在设置一个值
>>> widget.setRenderedValue(some_file) >>> widget() u'<a href="foo.txt">foo.txt</a>'
现在让我们转到编辑小部件。首先是一个添加表单,没有可用的数据,也没有请求中的数据
>>> from hurry.file.browser import EncodingFileWidget >>> field = File(__name__='foo', title=u'Foo', required=False) >>> field = field.bind(None) >>> request = TestRequest() >>> widget = EncodingFileWidget(field, request) >>> def normalize(s): ... return '\n '.join(filter(None, s.split(' '))) >>> print normalize(widget()) <input class="fileType" id="field.foo" name="field.foo" size="20" type="file" />
现在让我们尝试一种情况,其中请求中存在数据,但文件为空字符串
>>> request = TestRequest(form={'field.foo': u''}) >>> widget = EncodingFileWidget(field, request) >>> def normalize(s): ... return '\n '.join(filter(None, s.split(' '))) >>> print normalize(widget()) <input class="fileType" id="field.foo" name="field.foo" size="20" type="file" />
现在让我们在已有可用数据的情况下再次渲染。应该出现一个包含文件_id的额外隐藏字段
>>> widget.setRenderedValue(some_file) >>> print normalize(widget()) <input class="fileType" id="field.foo" name="field.foo" size="20" type="file" /> (foo.txt)<input class="hiddenType" id="field.foo.file_id" name="field.foo.file_id" type="hidden" value="Zm9vLnR4dAp0aGUgY29udGVudHM=" />
现在让我们再次渲染,这次请求中提供了文件数据。应该发生相同的事情
>>> request = TestRequest(form={'field.foo': FileUpload(some_file)}) >>> widget = EncodingFileWidget(field, request) >>> print normalize(widget()) <input class="fileType" id="field.foo" name="field.foo" size="20" type="file" /> (foo.txt)<input class="hiddenType" id="field.foo.file_id" name="field.foo.file_id" type="hidden" value="Zm9vLnR4dAp0aGUgY29udGVudHM=" />
现在让我们再次渲染,这次没有请求中的文件数据,但有一个id。同样,我们应该看到相同的
>>> request = TestRequest(form={'field.foo.file_id': ... 'Zm9vLnR4dAp0aGUgY29udGVudHM='}) >>> widget = EncodingFileWidget(field, request) >>> print normalize(widget()) <input class="fileType" id="field.foo" name="field.foo" size="20" type="file" /> (foo.txt)<input class="hiddenType" id="field.foo.file_id" name="field.foo.file_id" type="hidden" value="Zm9vLnR4dAp0aGUgY29udGVudHM=" />
如果有文件数据和id,则会发生其他事情。首先,让我们准备一些新文件
>>> another_file = HurryFile('bar.txt', 'bar contents')
由于编码文件小部件的实现,我们知道文件_id 将是“YmFyLnR4dApiYXIgY29udGVudHM=”。让我们用一个原始id发出一个带有新文件上传的请求
>>> request = TestRequest(form={'field.foo': FileUpload(another_file), ... 'field.foo.file_id': ... 'Zm9vLnR4dAp0aGUgY29udGVudHM='})
我们期望新文件是上传的文件
>>> widget = EncodingFileWidget(field, request) >>> print normalize(widget()) <input class="fileType" id="field.foo" name="field.foo" size="20" type="file" /> (bar.txt)<input class="hiddenType" id="field.foo.file_id" name="field.foo.file_id" type="hidden" value="YmFyLnR4dApiYXIgY29udGVudHM=" />
hurry.file变更
1.2.1 (2011-08-09)
修复HurryFiles中的错误不等式比较。
1.2 (2009-03-11)
添加一个“size”属性,它知道文件的大小(无论是否使用tramline或ZODB存储)
1.1 (2008-08-07)
在buildout.cfg中添加安装测试运行器的配置。
在setup.py中列出依赖项。
依靠zope.session而不是zope.app.session来停止弃用警告。
根据README.txt和file.txt doctests以及CHANGES.txt在setup.py中添加长描述。
1.0 (2006-10-25)
通过IFileRetrieval支持Tramline(快速文件上传/下载)。默认情况下,没有任何变化。
如果将TramlineFileRetrievalBase的子类注册为IFileRetrieval实用工具,hurry.file将变为Tramline感知。如果手动创建文件,可以通过createHurryFile函数创建,或者通过IFileRetrieval服务的‘createFile’方法创建。这将负责将文件存储在正确位置。
Tramline可以在以下位置找到:http://codespeak.net/svn/rr/tramline/trunk
0.9.3 (2006-10-23)
在重新显示小部件时返回 tramline_ok 头部,以防我们正在使用 tramline。
0.9.2 (2006-09-28)
Zope 3.3在处理文件名编码的方式上有所改变,这破坏了hurry.file。这包括一个解决方案。
0.9.1 (2006-09-22)
第一个cheeseshop发布。
0.9 (2006-06-15)
从hurry包中分离出来,成为hurry.file
egg化
0.8 (2006-05-01)
初始公开发布。