大道至简,新一代企业应用无栈开发

平台之上,一种语言,可视化、脚本化、全端一体化开发

文件库

文件夹、文件、快捷方式

docutils document without title

文件存放在文件夹中,可以创建快捷方式。文件支持版本管理,编辑文件会加锁防止冲突,可以有附件。

目录

1   文件夹 Folder

1.1   对象类型 object_types

文件夹的对象类型 obj.object_types

('Folder', 'Container')

文件夹是一种 容器 对象,拥有 容器 对象的所有接口。

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.1   文件的对象类型 object_types

文件的对象类型 obj.object_types:

('File', 'Item')

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.5   mime类型 mime_type

可通过 file_obj.mime_type 获取文件的mime类型,也可以修改:

file_obj.mime_type = 'text/plain'

2.6   文件内容 get_data

可读取文件内容:

file_obj.get_data()

文件内容不可以修改,如果要修改,可以利用版本管理,创建新版本。

2.7   文件的大小 get_size

文件的大小(字节数):

size = file_obj.get_size()

可以通过 ui.size(size) 人性化展示字节数。

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.2   创建快捷方式 add_shortcut

在文件中添加快捷方式:

shortcut = folder.add_shortcut(uid, name='')

5.3   快捷方式指向的对象 source_obj

可找到指向的文件:

source_obj = shortcut.source_obj

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'])

5.5   快捷方式的扩展属性

快捷方式自身,目前不支持设置扩展属性,而是直接显示指向文件(文件夹)的扩展属性。

快捷方式建立索引的时候,会使用指向快捷方式的对象扩展属性进行索引。

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.4   删除历史版本

删除一个版本:

del obj.revisions[revision_id]

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, # 是否是签章版本
}

其中:

  1. revision_id: 一个自动增长的字符串类型数字
  2. major_version/minor_version: 版本版次,可以通过 tag 方法修改
  3. uid: 这个是定版的永久UID

6.6   保存数据为新版本 save

也可以直接保存数据为新版本:

old_revision_id = obj.revisions.save(data='hello, world', filename=None)

其中:

  • data是新版本的文档的内容
  • filename是新的文件名,如果不填,和之前的文件名保持相同。
  • 返回值是上一个版本的ID,这是一个不断累加的数字(但是是字符串类型)

注意:

  1. 文档每次变更,默认保存为临时版本。
  2. 临时版本定期会自动清理,不会永久存储
  3. 如果上一个版本是一个空的临时文件,会自动删除,这时候返回 None
  4. 文件上传之后,会自动更新版本号。
  5. 如果文件保存历史版本之前已经是存档或者发布状态,那么文档的附件和关联关系会记录在这个历史版本中。

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: 可以传递一个定版时间,默认是当前时间

注意,签章版本:

  1. 禁止编辑和创建分支版本
  2. 合并检查冲突,会忽略签章版本

6.8   完整版本号 get_display_version

拼接 major_verson 和 minor_version,完整的版本号

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']

8.2   新建附件 add

添加一个附件:

obj.attachments.add(filename, data, mime_type=None)

8.3   移为附件 attach

将现有的文件转移为附件:

obj.attachments.attach(file_obj)

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']