文件库
文件夹、文件、快捷方式
文件存放在文件夹中,可以创建快捷方式。文件支持版本管理,编辑文件会加锁防止冲突,可以有附件。
目录
- 1 文件夹 Folder
- 2 文件 File
- 3 转换缓存文件 cache
- 4 文件下载和预览
- 5 快捷方式 ShortCut
- 6 版本管理 revisions
- 7 文件加解锁
- 8 附件 obj.attachments
- 9 文件夹设置
1 文件夹 Folder
1.2 文件夹的状态 stati
文件夹的状态 obj.stati 中,以 folder. 为前缀分为:
- folder.default : 普通文件夹
- folder.control : 受控文件夹,管理正式文件。受控文件夹的文件在发布或存档之前,是保密的。
1.3 创建子文件夹 add_folder
文件夹用来存放文件和文件的快捷方式,文件夹还能存放子文件夹:
sub_folder = folder.add_folder(name, title='', description='', order_limt=300)
其中:
title:文件夹名字
description:文件夹的描述
order_limit:文件夹可排序的子项限制。
如果不为0,表示文件夹采用拖动文件排序。如果不允许排序,可以:
folder.set_order_limit(0)
1.4 附件文件夹
评注等默认附件存放在如下文件夹:
obj.settings['attachments_folder']
在表单中使用附件文件夹作为默认位置:
container.settings.get('attachments_folder', container, inherit=True)
2 文件 File
2.2 文件的title
通常来说或文件的name和title是一致的,内部会自动维护。
不能直接更改文件的title,否则导致和name不一致。
如果要修改title,只能通过 obj.move_to的方法来改名。
2.3 文件的基本状态 stati
文件的状态 obj.stati 分2组:
visible. 前缀:保密与否
- visible.default 普通
- visible.private 保密,文件夹的普通查看人没有权限查看保密的内容
modify. 前缀:修改状态
对于工作版本,有如下状态:
- modify.default 草稿
- modify.archived 存档:一旦存档,文件禁止任何人删除、修改。
- modify.published 发布:一旦发布,文件禁止任何人删除、修改。
- modify.abandoned, '废弃', '文件已被废弃'
对于历史版本,有如下状态:
- modify.history_default 处于历史版本的普通文件
- modify.history_archived 处于历史版本的存档或发布文件
- modify.history_abandoned 处于历史版本的废弃文件
- modify.history_temp 外部编辑产生的临时历史版本,可能会被自动清理删除
2.4 添加文件 add_file
在文件夹下添加文件:
new_file = folder.add_file(name, data='', mime_type='')
其中:
- name:文件名,如 abc.txt
- data: 文件的内容
- mime_type: [可选]文件的mime类型,如 text/plain。如果没有,自动根据 name 计算。
2.8 文件的存储位置
文件根据站点的存放策略,存放在不同的存储设备中:
# 查看文件存储的设备 device = file_obj.mdfs_device # 存储的位置 key = file_obj.mdfs_key
3 转换缓存文件 cache
文件一旦上传,会自动转换成多种mime格式缓存起来,比如:
- 纯文本(text/plain),这个用于全文搜索、文件差异比较等
- pdf文件(application/pdf),这个用于下载水印pdf
- swf/h5等,用于文件预览
转换文件和原始文件分开存储。系统为每个原始存储设备,分配一个缓存存储设备。
文件的各种类型的转换文件,分别存放在转换目标mime命名的子文件夹下。
一个mime转换的转换结果,通常转换为一个名字为 transformed 的文件。 如 transformed.txt , transformed.pdf, transformed.swf
但也有很多转换,可能有多个转换文件,比如压缩包解压、文件分页预览等。下面以子文件 subfile 称呼这些转换文件。
3.1 转换是否生成 exists_cache
查看文件是否已经转换:
transformd = file_obj.exists_cache(mime)
这会自动查看该mime转换文件夹下 transformed.xxx 主文件是否存在。
也可以查看子文件是否存在:
transformd = file_obj.exists_cache(mime, subfile)
3.2 读转换文件 get_cache
如果已经转换,可以读取转换文件的内容:
if file_obj.exists_cache(mime, subfile): data = file_obj.get_cache(mime, subfile)
subfile参数可选。如果直接 file_obj.get_cache(mime) ,表示读取 transformed.xxx 这个主文件。
如果需要读取文件的属性信息,如doc/mp3/mp4的信息:
file_obj.get_cache('application/x-metadata-json')
3.3 删除转换文件 remove_cache
也可以删除某个转换缓存文件:
file_obj.remove_cache('application/x-metadata-json', subfile='')
4 文件下载和预览
不换是原始文件,还是转换缓存文件,都可以下载和预览
4.1 文件下载 download_url
文件的下载是通过云查看服务器实现的。可以设定权限生成一个临时下载地址(通常有效1小时):
download_url = file_obj.download_url(request, mime='text/html', subfile='', disposition='attachment')
其中:
- mime 为空,表示下载原始文件,否则表示转换之后的文件
- subfile 表示转换之后的子文件,比如解压压缩包之后,压缩包里面的某个文件
- disposition 下载的消息头参数
- attachment:下载为文件
- inline:内嵌到网页
使用上面的地址下载,会自动检查权限,并记录日志,最后跳转到云查看服务器下载。
4.2 免鉴权下载 direct_download_url
也可以跳过权限,以及下载日志,方法为:
download_url = file_obj.direct_download_url(mime='text/html', subfile='', expires_time=60*60*4, disposition='attachment', check_permission=False)
其中:
- mime 表示下载某种转换格式的文件,为空表示下载原始文件
- subfile:如果下载转换文件,这里是转换文件的名字
- expires_time:下载地址多长时间有效
- disposition:下载的消息头
- attachment:表示下载到桌面
- inline:表示直接在浏览器中显示
- check_permission 应该设置为 False
4.3 文件预览 preview_url
也可以直接生成云查看预览地址,使用系统内置的文件查看器在线查看文档:
file_obj.preview_url(watermark_text="", allow_copy=True, allow_print=True, allow_pdf=True, allow_download=True, expire=None, paged_excel=False, flash_first=False, show_top=True, viewer_width="100%", viewer_height="100%", watermark_color="", watermark_size="", pages=[])
其中:
- watermark_text:水印文字
- watermark_color: 水印的颜色
- watermark_size:水印字体大小
- allow_copy:是否允许复制文字
- allow_print:是否允许打印
- allow_pdf:是否允许下载pdf
- allow_download:是否允许下载
- expire: 预览链接什么时候失效(时间戳)
- paged_excel: excel采用何种分页方式预览
- flash_first:优先采用Flash查看器
- show_top:显示顶部文件名工具条
- viewer_width: 查看器宽度
- viewer_height: 查看器高度
- pages: 允许查看哪些页面,默认查看所有页面
4.4 文件水印
站点可以设置文件水印
得到水印设置:
watermark_settings = root.call_script('zopen.views:watermark', context=file_obj, request=request)
返回水印信息:
{ 'text': '...', # 水印文字 'font': '宋体', # 字体,默认宋体 'color': '#110102', # 水印的颜色 'size': '14', # 水印字体大小 }
5 快捷方式 ShortCut
5.1 对象类型 object_types
对于文件快捷方式, 其object_types为:
('FileShortCut', 'Item')
对于文件夹快捷方式,object_types:
('FolderShortCut', 'Item')
5.4 修改快捷方式指向 source
快捷方式指向存放在 source 属性中:
source_uid = shortcut.source
快捷方式可以指向其他的文件或者文件夹:
short_cut.source = root.object_uid(obj_2)
如果希望设置obj当前的定版版本,可以:
shortcut.source = obj.revisions.get_fixed_uid()
可以通过搜索查找某个文件的所有快捷方式:
results = QuerySet(restricted=False).filter(source=uid)\ .anyof(object_types = ['FileShortCut', 'FolderShortCut'])
6 版本管理 revisions
文件的内容不能直接修改,每次内容变化,都会产生一个历史版本。
每个文件可以包含:
- 一个工作版本:这个版本的uid永远不变
- 多个历史版本:文件附带一个类型为 Versions 的容器,存放了所有的历史版本
文件的历史版本,禁止单独授权更改保密状态,自动和主文件保持一致。
6.1 历史版本的变更状态
历史版本有一组专有的更改状态:
- modify.history_default 处于历史版本的普通文件
- modify.history_archived 处于历史版本的存档或发布文件
- modify.history_abandoned 处于历史版本的废弃文件
- modify.history_temp 外部编辑产生的临时历史版本,可能会被自动清理删除
6.2 文件的历史版本容器 (dict接口)
可以判断某个文件是否是历史文件:
>>> 'Versions' in obj.parent.object_types
可以通过 len(obj.revisions) 查看历史版本的数量。
6.2.1 查看全部历史版本 values
也可以得到全部历史版本:
for obj in obj.revisions.values(include_temp=False): print obj.name print obj.md['title']
其中 include_temp 表示是否包含临时版本
6.2.2 全部历史版本代号 keys
只查出版本ID,这个性能比 values 更快:
>>> obj.revisions.keys(include_temp=True) ['1', '2', '4', '5']
其中 include_temp 表示是否包含临时版本
6.2.3 得到历史版本对象
可以获取历史版本对象:
obj = obj.revisions[revision_id]
其中切分参数revision_id是版本ID,比如 '2' 如果不存在返回None
6.2.5 历史版本的文件名和name
注意,对于文件的历史版本, obj.name 不再是文件名,而是 revision_id。
>>> obj.name 2
而是如果要得到文件名,应该是:
>>> obj.md['title'] 'abc.docx'
6.3 回滚到某个版本 revert
obj.revisions.revert(revision_id)
6.4 文件工作版本的固定uid: get_fixed_uid
对于工作版本,随着版本的变化,文件的内容发生变化。
对于快捷方式,需要固定指向当前内容。如果有新版本,就切换到指向历史版本。
为满足这个徐,工作版本的内容提供一个固定uid,如果成为历史版本,自动成为历史版本的uid:
fixed_uid = file_obj.revisions.get_fixed_uid()
这个固定uid,是在首次调用 get_fixed_uid 的时候生成的。
6.5 版本信息 info
不论是工作版本,还是历史版本,都可以查看版本信息:
version_info = file_obj.revisions.info(version_id=None)
参数 version_id 是可选的,默认是当前工作版本的信息,可传递指定版本的查看信息
返回内容为:
{'revision_id' : '2', # 版本ID 'major_version' : '1', # 版本号 'minor_version' : '0', # 版次号 'uid' : 123123123, # 版本修改人 'pid' : 'users.panjy', # 版本修改人 'timestamp' : 12312312.123, # 版本修改时间 'filename' : 'abcd.doc', # 原始文件名 'comment' : 'some comments', # 版本说明 'is_signed_doc': False, # 是否是签章版本 }
其中:
- revision_id: 一个自动增长的字符串类型数字
- major_version/minor_version: 版本版次,可以通过 tag 方法修改
- uid: 这个是定版的永久UID
6.6 保存数据为新版本 save
也可以直接保存数据为新版本:
old_revision_id = obj.revisions.save(data='hello, world', filename=None)
其中:
- data是新版本的文档的内容
- filename是新的文件名,如果不填,和之前的文件名保持相同。
- 返回值是上一个版本的ID,这是一个不断累加的数字(但是是字符串类型)
注意:
- 文档每次变更,默认保存为临时版本。
- 临时版本定期会自动清理,不会永久存储
- 如果上一个版本是一个空的临时文件,会自动删除,这时候返回 None
- 文件上传之后,会自动更新版本号。
- 如果文件保存历史版本之前已经是存档或者发布状态,那么文档的附件和关联关系会记录在这个历史版本中。
6.7 文件定版 tag
可以对文档定版打上版本号,一旦定版,版本就是正式版本,可永久存储:
obj.revisions.tag(revision_id=None, major_version=None, minor_version=None, comment='', timestamp=datetime.utcnow()):
- revision_id:[可选]定版对象,默认对工作版本进行定版,也可以对历史版本定版
- major_version:[可选]大版本号,默认沿用上一个版本的
- minor_version:[可选]小版本号,默认自动增长
- comment:版本说明信息
- timestamp: 可以传递一个定版时间,默认是当前时间
注意,签章版本:
- 禁止编辑和创建分支版本
- 合并检查冲突,会忽略签章版本
6.8 完整版本号 get_display_version
拼接 major_verson 和 minor_version,完整的版本号
6.9 查找最新版本 head
head()得到最新的工作版本对象:
>>> obj.revisions.head() is obj
也可以找到最新的定版版本:
>>> obj.revisions.head(tagged=True) is obj
6.10 创建分支版本 fork
如果对原始文件没有直接修改权限,则需要通过分支、合并的方法来做。
fork一个文档进行修改, 实际上就是拷贝文档到新的位置:
>>> forked = obj.revisions.fork(container, new_name='', revision_id=None)
其中:
- container:分支版存放的位置
- new_name:分支版本的名字,如果不填,基于现有文件的文件名自动生成
- revision_id:分支版本的基准版本,如果不填,基于最新版本创建分支
分支采用关系记录,可以查看所有分支版本:
>>> forked.relations.list_sources('fork') >>> forked.relations.list_targets('fork')
可以查看当时分支的版本号:
>>> forked.relations.get_metadata('fork', obj) {'revision':2}
6.11 合并分支版本 merge
编辑完成,可以合并版本:
>>> forked.revisions.merge(obj)
默认合并和删除原始文件,也可以保留:
>>> forked.revisions.merge(obj, keep_source=True)
如果版本冲突,会有异常抛出:
>>> forked.revisions.merge(obj) VersionConflicted: ...
6.12 合并冲突 start_resolve/resolve
则需要开始进行手工合并:
>>> forked.revisions.start_resolve()
此时可以检查冲突信息(resolve是需要合并的版本):
>>> forked.relations.get_metadata('fork', obj) {'revision':2, 'resolve':3}
手工合并完成,并作出标识,解决冲突:
>>> forked.revisions.resolve() >>> forked.relations.get_metadata('fork', obj) {'revision':3, 'resolve':3} >>> forked.revisions.merge(obj)
如果冲突解决期间,obj再次发生变化,仍然可能导致merge失败, 需要再次合并:
>>> forked.revisions.merge(obj) VersionConflicted: ... >>> forked.revisions.start_resolve() (手工合并obj和obj的内容差异) >>> forked.revisions.resolve() >>> forked.revisions.merge(obj)
注意:即便forked和obj之间没有fork关系,也可以直接保存为新版本。 merge之后obj不会被删除,如果不需要,可以再次手工删除。
6.13 为版本附加信息 annotate
file_obj.annotate(**kw)
这个信息是附加在版本之上的,之后当前工作版本变成历史版本,或者历史版本回滚到最新版本,这个信息都不会丢失。
例子:
# 设置附加信息 aaa, bbb: file_obj.revisions.annotate(aaa=1, bbb=2) # 读取附加信息: print file_obj.revisions.info()['annotations']['aaa'] print file_obj.revisions.info()['annotations']['bbb']
7 文件加解锁
多人编辑文件,为了避免冲突,在编辑之前进行加锁,编辑完成会自动解锁。
7.1 加锁 lock
文档编辑人可以加锁:
doc.lock(owner=request.me.id, shared=False, annotation={})
其中:
owner: 加锁人
shared:是否是共享锁(shared),默认False表示排他锁(exclusive)
annotation: 附加的锁信息,可以用于多人编辑:
{'editor':'zopen.onlyoffice:editor_onlyoffice', 'session':'1231-12312' }
返回:加锁是否成功
加锁成功的条件:
- 如果未加锁,可以加任何锁
- 如果加排他锁,不可以加任何锁
- 如果加共享锁,只能加共享锁
7.2 解锁 unlock
编辑完成,可以解锁:
doc.unlock(owner=request.me.id, force=False)
其中:
- owner: 对谁解锁
- 如果共享编辑,只会解锁当前人员
- 如果全部人员都解锁,文档完成解锁
- force:是否强制解锁
返回:如果解锁是否成功
7.3 查看锁 get_lock
可以查看锁信息(加锁人和时间):
lock = doc.get_lock()
lock 是一个dict类型的信息,形式如下:
{'pids': ['users.aaa'], #加锁人 'shared': False, # 是否shared 'timestamp': 12312312, 加锁时间 'annotation': { # 锁信息 'editor':'zopen.onlyoffice:editor_onlyoffice', 'session':'1231-12312' } }
8 附件 obj.attachments
文件可以直接保存附件:
- 附件直接附加在主文件后,附件的权限和主文件相同
- 主文件移动,附件跟随移动
- 在文件夹中,默认无法看到附件
- 删除主文件,附件跟随被删除
文件的附件,禁止单独授权更改保密状态,自动和主文件保持一致。
8.1 附件容器 (dict接口)
所有附件统一存放在主文件的一个附加容器中。
判断一个文件是否是附件:
'Attachments' in obj.parent.object_types or ('Versions' in obj.parent.object_types and 'Attachments' in obj.parent.parent.parent.object_types)
查看附件的数量(这也可以用于判断一个文件是否包含附件):
len(obj.attachments)
所有的附件:
obj.attachments.values()
全部附件名称:
obj.attachments.keys()
删除一个附件:
del obj.attachments['abc.doc']
9 文件夹设置
设置排序:
folder.settings.set('sort_by', {'sort_index':'title', 'reverse':True})
默认文件扩展属性:
folder.settings['container_mdsets'] = ['zopen.cms:top']
默认文件夹扩展属性:
folder.settings['item_mdsets'] = ['zopen.cms:top']