跳转到主要内容

ZODB的校验和

项目描述

plone.checksum

概述

ZODB数据校验和

通用

此包定义了一个用于计算、访问和将校验和写入对象单个字段的ChecksumManager。让我们创建一个Archetypes Document内容对象

>>> folder = self.folder
>>> folder.invokeFactory('Document', 'mydocument', title='My Document')
'mydocument'
>>> doc = folder.mydocument

现在我们可以像这样请求一个对象(如)的ChecksumManager

>>> from plone.checksum import IChecksumManager
>>> manager = IChecksumManager(doc)

管理器将字段名映射到IChecksum对象

>>> sorted(manager.keys())
['allowDiscussion', 'contributors', 'creation_date', 'creators', 'description', 'effectiveDate', 'excludeFromNav', 'expirationDate', 'id', 'language', ..., 'text', 'title']

我们将对象标题的校验和保留为original,以便在下面的测试中使用

>>> original = str(manager['title'])
>>> print original
f796979e29808c04f422574ac403baeb

我们可以使用校验和对象的calculate方法手动调用校验和计算。此时,存储的和计算出的校验和应该肯定是一样的

>>> print manager['title'].calculate()
f796979e29808c04f422574ac403baeb

使用update方法写入校验和(并将其附加到具有该字段的对象)

>>> manager['title'].update('something else')
>>> print manager['title']
something else

现在让我们使用校验和管理的update_checksums方法恢复到正确的校验和

>>> manager.update_checksums()
>>> print manager['title']
f796979e29808c04f422574ac403baeb

最后,我们将更改标题并验证校验和是否已更改

>>> doc.setTitle('something else')
>>> print manager['title'].calculate()
6c7ba9c5a141421e1c03cb9807c97c74

然而,存储的校验和仍然是旧值。我们需要通过再次触发修改事件来修复这个问题。这次,我们不会自己触发事件,而是调用 processForm,它会帮我们触发事件

>>> print manager['title']
f796979e29808c04f422574ac403baeb
>>> doc.processForm()
>>> print manager['title']
6c7ba9c5a141421e1c03cb9807c97c74

顺便说一下,这等于

>>> import md5
>>> print md5.new('something else').hexdigest()
6c7ba9c5a141421e1c03cb9807c97c74

文件

让我们创建一个文件内容对象:之后,我们查看 file 字段的校验和

>>> from StringIO import StringIO
>>> folder.invokeFactory('File', 'myfile')
'myfile'
>>> file = folder.myfile
>>> manager = IChecksumManager(file)
>>> print manager['file']
d41d8cd98f00b204e9800998ecf8427e

让我们用一些内容填充内容对象的 file 字段

>>> contents = StringIO('some contents')
>>> file.setFile(contents)
>>> print manager['file'].calculate()
220c7810f41695d9a87d70b68ccf2aeb

如果我们将文件的内容设置成其他东西,校验和就会改变

>>> contents = StringIO('something else')
>>> file.setFile(contents)
>>> print manager['file'].calculate()
6c7ba9c5a141421e1c03cb9807c97c74

同样,这也适用于较大的文件。注意,这里的内部存储结构是不同的

>>> contents = StringIO('some contents, ' * 10000)
>>> file.setFile(contents)
>>> print manager['file'].calculate()
8d43d3687f3684666900db3945712e90

让我们再次确认,当我们设置另一个大文件时,校验和会改变。这次我们将使用 PUT 方法上传文件,并确保触发了校验和计算

>>> from Products.Archetypes.tests.utils import aputrequest
>>> contents = StringIO('something else, ' * 10000)
>>> request = aputrequest(contents, 'text/plain')
>>> request.processInputs()
>>> ignore = file.PUT(request, request.RESPONSE)
>>> str(file.getFile()) == contents.getvalue()
True
>>> print manager['file']
4003a21edc0b8d93bda0ce0c4fa71cfa

这又与

>>> print md5.new(contents.getvalue()).hexdigest()
4003a21edc0b8d93bda0ce0c4fa71cfa

BlobFile 支持

一些设置

>>> import md5
>>> from StringIO import StringIO
>>> from plone.checksum import IChecksumManager
>>> from Products.BlobFile.Extensions.install import install
>>> dontcare = install(self.portal)

实际测试

>>> folder.invokeFactory('BlobFile', 'myblob')
'myblob'
>>> blob = folder.myblob
>>> manager = IChecksumManager(blob)
>>> print manager['file']
n/a
>>> print manager['file'].calculate()
d41d8cd98f00b204e9800998ecf8427e

让我们用一些内容填充内容对象的 file 字段

>>> contents = StringIO('some contents, ' * 10000)
>>> blob.setFile(contents)
>>> print manager['file'].calculate()
8d43d3687f3684666900db3945712e90

如果我们将文件的内容设置成其他东西,校验和就会改变

>>> contents = StringIO('something else, ' * 10000)
>>> blob.setFile(contents)
>>> print manager['file'].calculate()
4003a21edc0b8d93bda0ce0c4fa71cfa
>>> print md5.new(contents.getvalue()).hexdigest()
4003a21edc0b8d93bda0ce0c4fa71cfa

用户界面

check_all 列出了 ZODB 中存储的校验和与即时计算的校验和不同的项目

>>> self.loginAsPortalOwner()
>>> check_all = self.portal.unrestrictedTraverse('checksum__check_all')
>>> print check_all() # doctest: +ELLIPSIS
The following items failed the checksum test:
...

在我们的新创建的门户中,有很多对象没有触发修改事件。让我们使用其他视图 update_all 来将所有对象的校验和设置为计算值

>>> update_all = self.portal.unrestrictedTraverse('checksum__update_all')
>>> print update_all()
Calculated and stored checksums of ... items.

现在,check_all 应该会给出绿灯

>>> print check_all()
All ... objects verified and OK!

我们可以使用 print_all 视图生成小报告。比如说,我们对门户网站中所有对象的 title 字段的校验和感兴趣

>>> request = self.portal.REQUEST
>>> print_all = self.portal.unrestrictedTraverse('checksum__print_all')
>>> request.form['checksum_fields'] = ['title']
>>> print; print print_all()
<BLANKLINE>
...
a47176ba668e5ddee74e58c2872659c7 http://nohost/plone/front-page :title
...

我们也可以按我们的需求格式化输出。可用的键有

>>> output_form = ('%(checksum)s %(url)s %(fieldname)s '
...                '%(content_type)s %(filename)s')
>>> request.form['checksum_output'] = output_form

请注意,content_type 只适用于文件。而 filename 目前只适用于 OFSBlobFile 值,来自 blob 产品。

这次我们将创建一个包含所有 File 内容对象的标题字段的报告

>>> request.form['checksum_fields'] = ['title']
>>> request.form['portal_type'] = 'File'
>>> print print_all()

哦,没有文件。让我们修复这个问题。我们将创建一个假的 GIF 文件

>>> contents = 'GIF89a xxx'
>>> self.folder.invokeFactory('File', 'myfile', file=contents)
'myfile'
>>> print print_all()
d41d8cd98f00b204e9800998ecf8427e http://nohost/plone/Members/test_user_1_/myfile title n/a n/a

当我们请求 'file' 字段的报告时,我们会在输出中获得额外的 content_type 字段

>>> request.form['checksum_fields'] = ['file']
>>> print print_all()
e429b46baca83aa4a713965f5146f31a http://nohost/plone/Members/test_user_1_/myfile file image/gif n/a

这是我们期望的吗?是的,这就是

>>> import md5
>>> print md5.new('GIF89a xxx').hexdigest()
e429b46baca83aa4a713965f5146f31a

如果您想获取门户网站中所有 BlobFiles 的 md5sum 兼容报告,请访问

http://myportal/checksum__print_all?portal_type=BlobFile&checksum_fields:list=file&checksum_output=%(checksum)s%20%20%(filename)s

CMFEditions 支持

plone.checksum 在查询、更新和打印操作中支持 CMFEditions,因为它们会考虑项目的版本,而这些项目在普通目录搜索中不会显示。

让我们做一些通用设置

>>> self.loginAsPortalOwner()
>>> from plone.checksum import IChecksumManager
>>> request = self.folder.REQUEST
>>> repository = self.portal.portal_repository

让我们创建一个文档,并为其创建一个版本

>>> self.folder.invokeFactory('Document', 'mydocument')
'mydocument'
>>> doc = self.folder.mydocument
>>> doc.setTitle('First version')
>>> repository.applyVersionControl(doc)

现在我们将修改文档并保存当前版本。之后,我们应该有两个版本

>>> doc.setTitle('Second version')
>>> repository.save(doc)
>>> history = repository.getHistory(doc)
>>> print history[0].object.Title()
Second version
>>> print history[1].object.Title()
First version
>>> len(history)
2

让我们使用 update_all 视图方法更新所有校验和

>>> update_all = self.portal.unrestrictedTraverse('checksum__update_all')
>>> print update_all()
Calculated and stored checksums of ... items.

然而,print_all 返回了第一个版本的错误校验和

>>> print_all = self.portal.unrestrictedTraverse('checksum__print_all')
>>> request.form['checksum_fields'] = ['title']
>>> request.form['path'] = '/'.join(doc.getPhysicalPath())
>>> print print_all()
cd9dc5fb4185366e3f551f325c572288 http://nohost/plone/Members/test_user_1_/mydocument :title
d41d8cd98f00b204e9800998ecf8427e http://nohost/plone/Members/test_user_1_/mydocument :title

为什么这样呢?这是因为我们没有一开始就给我们的文档一个标题,所以生成的校验和是空字符串的。update_all 不接触旧版本。如果它要这样做,它就必须再次存储旧版本。因此,通常我们不会担心更新旧版本的校验和。

现在让我们创建另一个版本。当第三个版本到位时,运行 update_all,我们将看到在 print_all 中最后两个版本有校验和。这是因为我们在第二个版本是活动版本时运行了 update_all。通常,通过网站,每个更改都会触发修改事件,因此您不需要担心这个问题,它会正常工作。

>>> doc.setTitle('Third version')
>>> repository.save(doc)

在我们继续之前,让我们确保我们可以检索第二个版本并获取其校验和

>>> second_version = repository.retrieve(doc, 1).object
>>> print second_version.Title()
Second version
>>> print str(IChecksumManager(second_version)['title'])
cd9dc5fb4185366e3f551f325c572288

现在我们更新所有校验和并打印它们

>>> print update_all()
Calculated and stored checksums of ... items.
>>> print print_all()
26b9d2c5bb8820c1c6de354c9015b2a1 http://nohost/plone/Members/test_user_1_/mydocument :title
cd9dc5fb4185366e3f551f325c572288 http://nohost/plone/Members/test_user_1_/mydocument :title
n/a http://nohost/plone/Members/test_user_1_/mydocument :title

这是我们期望的吗?是的,这就是

>>> import md5
>>> print md5.new('Third version').hexdigest()
26b9d2c5bb8820c1c6de354c9015b2a1
>>> print md5.new('Second version').hexdigest()
cd9dc5fb4185366e3f551f325c572288

变更日志

0.1

首次公开发布

项目详情


下载文件

下载适用于您平台的文件。如果您不确定选择哪个,请了解有关 安装包 的更多信息。

源代码发行版

plone.checksum-0.1.tar.gz (18.7 kB 查看哈希值)

上传时间 源代码

构建发行版

plone.checksum-0.1-py2.4.egg (23.1 kB 查看哈希值)

上传时间 源代码