dotnet OpenXML 为什么资源使用 Relationship 引用
在 OpenXML 文档格式里面,所有的资源以及页面之间的引用等,都是通过 Relationship 的引用,如资源需要通过 GetReferenceRelationship 的方法才能拿到。那为什么要这样设计呢
在做 Office 解析,可以看到资源的引用,如图片的引用等,不是应用相对的文件路径,而是使用 r:id="xx"
的方式引用,而实际的引用文件需要在 xx.rels 文件里面才能找到引用的路径
尽管在 OpenXML SDK 里面这些细节已经被封装好了,只需要通过 GetReferenceRelationship 方法就可以拿到对应的资源,但我好奇为什么 Office 这样设计
在 Office 文档解析 文档格式和协议 我和小伙伴讲了 Office 文档的格式,这里存储的方式使用的是 OPC (Open Package Convention) 协议
在 OPC 协议里面要求多个 Part 也就是文件之间不能相互引用,如果两个 Part 有引用,需要在 Part 的 rels
文件里面添加引用,而在 Part 里面只使用对应的 rels
文件的记录资源的 Id 的值
那 Part 的 rels
又是什么?在 OPC 里面规定的 Part 可以理解为文件,因为 OPC 是基于 Zip 的打包方法,而 Zip 里面都是文件。而 rels
其实就是 OPC 里面的 Relationship 概念,这个 Relationship 是一种特殊的 Part 文件,它描述了各 Part 之间的依赖关系。根据OPC协议的规定,所有的 Relationship 都必须存储在名为 _rels
的文件夹中,并且所有 Relationship 的文件名都必须以 .rels
为后缀。每个 Part 可以根据自身的业务需求有一个对应的 Relationship 文件,这个对应的 Relationship 文件必须存放在这个 Part 文件所在文件夹的 _rels
文件夹里面,同时要求使用 Part 文件加上 .rels
后缀,不能使用其他名字
如某个 PPT 页面 slide1.xml 引用了某个音频文件,那么这个页面不能直接写音频文件的相对路径,而是需要在 slide1.xml 所在文件夹新建一个 _rels
文件夹,在里面放一个 slide1.xml.rels
文件,如下
pptslidesslide1.xml
pptslides_relsslide1.xml.rels
按照 OPC 的定义,在 Relationship 里面定义引用,假设音频文件存放在 pptmediaimage1.png
文件,那么对应的对应的 slide1.xml.rels
文件内容可以如下
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<Relationships xmlns="http://schemas.openxmlformats.org/package/2006/relationships">
<Relationship Id="rId1" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/image" Target="../media/image1.png"/>
</Relationships>
使用 Relationship
定义 Id 的值,用来给 slide1.xml
引用,同时配置资源类型,通过 Type 定义,最后使用 Target 属性引用文件
此时在 slide1.xml
就可以根据 Id 引用资源,如以下代码
<a:blip r:embed="rId1"/>
此时通过 rId1
就可以在 slide1.xml.rels
找到对应的资源,然后通过资源的相对路径拿到文件
在 OpenXML SDK 里面将这部分都封装了,不需要咱自己去找对应的文件,通过 GetPartById 或 GetReferenceRelationship 传入资源的 Id 就可以拿到对应的资源。在 OPC 里面的定义,可以知道使用 Part 表示文件等。因此 GetReferenceRelationship 返回的是 ReferenceRelationship 类,根据对象转换为 DataPartReferenceRelationship 或 ExternalRelationship 等
在 2.11 版本的 DocumentFormat.OpenXml 库里面添加了我的代码,可以使用 TryGetPartById 方法在 OpenXmlPartContainer 尝试获取资源。因为默认的 GetPartById 将会在找不到资源的时候抛出 ArgumentOutOfRangeException 而如果文档是用户创建的,也许他用的是 WPS 等软件做的文档不遵守标准,此时就会炸了
if (Slide.SlidePart.TryGetPartById(id, out var part))
{
}
那为什么只有 GetPartById 添加了 TryGetPartById 方法,而 GetReferenceRelationship 没有?在获取不到资源的时候,会在 GetReferenceRelationship 里面抛出 KeyNotFoundException 提示
原因是使用 GetReferenceRelationship 时,一般都可以确定 Id 是否存在,因为有 HyperlinkRelationships 和 DataPartReferenceRelationships 等属性的存在,可以通过这些属性进行判断
关于 Relationship 的一个应用请看 C# dotnet 使用 OpenXml 解析 PPT 里面的视频
更多请看 Office 使用 OpenXML SDK 解析文档博客目录
本文会经常更新,请阅读原文: https://blog.lindexi.com/post/dotnet-OpenXML-%E4%B8%BA%E4%BB%80%E4%B9%88%E8%B5%84%E6%BA%90%E4%BD%BF%E7%94%A8-Relationship-%E5%BC%95%E7%94%A8.html ,以避免陈旧错误知识的误导,同时有更好的阅读体验。
- T-SQL Enhancement in SQL Server 2005[下篇]
- JS原型,a和b是不是失散多年的兄弟?
- Linux shell 程序设计1——安装及入门
- 偶遇--《坑新人--前端专用面试题》
- 简单的说下,(function(){...})() 与 (function(){...}()) 有什么区别?
- ASP.NET Process Model之二:ASP.NET Http Runtime Pipeline[上篇]
- Shell常用命令小结
- 插入法排序
- ASP.NET Process Model之二:ASP.NET Http Runtime Pipeline - Part II
- 震惊了!这样的js面试题让所有人-男默女泪
- 前端知识学了却不会用,都是没走心
- var a="xx";a=a+"ss";a的值变了,但"xx"字符串并没有变
- 先行者计划--1109微课总结 | 《通过二个demo初识webPack》
- 先行者计划--1107微课 《什么是Vuex?》| 文字简版
- JavaScript 教程
- JavaScript 编辑工具
- JavaScript 与HTML
- JavaScript 与Java
- JavaScript 数据结构
- JavaScript 基本数据类型
- JavaScript 特殊数据类型
- JavaScript 运算符
- JavaScript typeof 运算符
- JavaScript 表达式
- JavaScript 类型转换
- JavaScript 基本语法
- JavaScript 注释
- Javascript 基本处理流程
- Javascript 选择结构
- Javascript if 语句
- Javascript if 语句的嵌套
- Javascript switch 语句
- Javascript 循环结构
- Javascript 循环结构实例
- Javascript 跳转语句
- Javascript 控制语句总结
- Javascript 函数介绍
- Javascript 函数的定义
- Javascript 函数调用
- Javascript 几种特殊的函数
- JavaScript 内置函数简介
- Javascript eval() 函数
- Javascript isFinite() 函数
- Javascript isNaN() 函数
- parseInt() 与 parseFloat()
- escape() 与 unescape()
- Javascript 字符串介绍
- Javascript length属性
- javascript 字符串函数
- Javascript 日期对象简介
- Javascript 日期对象用途
- Date 对象属性和方法
- Javascript 数组是什么
- Javascript 创建数组
- Javascript 数组赋值与取值
- Javascript 数组属性和方法
- 在dbcolinux上安装cozy-light
- 在群晖docker上装elmlang可视调试编码器ellie
- Elasticsearch:inverted index,doc_values及source
- 在群晖docker上构建私有云IDE和devops构建链
- 小白学PyTorch | 14 tensorboardX可视化教程
- Apache Solr 漏洞复现
- Elasticsearch rollover API
- 重发和重定向有什么区别与重定向应用
- 为tinycolinux制作应用包
- CrossC2的2.0版本
- 使用OpenCV和Python计算图像的“色彩”
- 为tinycolinux创建应用包-toolchain和编译方法
- [译]在Solidity中如何优化Gas第一部分:变量
- [译]Solidity 0.7.0 新变化
- 两个数组的交集 II