XMP 元数据:SWF Metadata Tag 与 Dublin Core
有些 SWF 会把标题、作者和说明藏在一段 XMP 里;这里看看 Tag 77、Dublin Core,以及作品页究竟读到了什么。
Flash 保存计划 · 最近整理于 2026年7月30日
拆开一只 SWF,有时会在画面、声音和脚本之外,碰到一小段 XML。作者或制作软件可能在这里留下标题、创作者、说明、日期、工具和权利信息。装它的地方叫 Metadata(Tag 77),内容通常按 Adobe 的 XMP 方式组织。
作品页显示的 dc:title、dc:creator 和 dc:description 就来自这里。可以把它们看成随文件漂流至今的标签纸:很有价值,但仍是文件自己的说法,不是馆方替作者和版权盖过章的结论。
XMP 是什么
XMP 是 Adobe 提出的 Extensible Metadata Platform。它把两件事分开处理:一边描述“有哪些属性、值怎样组织”,另一边决定整包元数据要塞进 JPEG、PDF、视频或 SWF 的什么位置。实在不适合嵌进文件,也可以把它单独放成 sidecar 文件。
所以 XMP 并不只等于标题、作者、说明这几个字段。一个 packet 可以混用 Dublin Core、XMP Basic、XMP Media Management、Photoshop,甚至自定义命名空间。常见外形大概是这样:
<?xpacket begin="..."?>
<x:xmpmeta xmlns:x="adobe:ns:meta/">
<rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#">
<rdf:Description
rdf:about=""
xmlns:dc="http://purl.org/dc/elements/1.1/"
>
<!-- 各命名空间的属性 -->
</rdf:Description>
</rdf:RDF>
</x:xmpmeta>
<?xpacket end="w"?>
外面的 <?xpacket ...?> 是 packet wrapper,x:xmpmeta 包住 XMP,真正的 RDF 描述则在 rdf:RDF 和 rdf:Description 里。老文件未必排得这么整齐:属性顺序可以变,XML 可以压得很紧,命名空间前缀也可以换。dc 只是大家习惯用的简称,真正认身份的是它绑定的 URI。
还有一点值得记住:XMP 不是加密,也不是数字签名。能改 SWF 的工具同样能改这张标签纸。要核验文件有没有变化,仍要比对可信摘要或签名;要确认来源和作者,也得回到抓取记录、可靠署名等其他证据。
Dublin Core 三个常见字段
Dublin Core 是一组通用的资源描述词汇。XMP 把它放在 http://purl.org/dc/elements/1.1/ 命名空间下;本馆目前挑出最适合直接阅读的三个字段:
| 字段 | DCMI 基本含义 | Adobe XMP 值类型 | 常见 RDF 容器 |
|---|---|---|---|
dc:title |
给予资源的名称 | Language Alternative | rdf:Alt |
dc:creator |
对资源创作负主要责任的实体 | ProperName 的有序数组 | rdf:Seq |
dc:description |
对资源内容的说明或描述 | Language Alternative | rdf:Alt |
这里的 creator 可以是个人、组织或服务,不一定是实名作者,也不是上传者、版权人或制作软件的别名。工具名称更常写在 xmp:CreatorTool;作品页上的“制作工具”还可能来自 SWF 自己的 ProductInfo。
dc:title 与 dc:description:语言替代
标题和说明经常不止一种语言。XMP 通常用 rdf:Alt 把它们排在一起,每个 rdf:li 用 xml:lang 标出语言:
<dc:title>
<rdf:Alt>
<rdf:li xml:lang="x-default">Space Game</rdf:li>
<rdf:li xml:lang="en-US">Space Game</rdf:li>
<rdf:li xml:lang="zh-CN">太空游戏</rdf:li>
</rdf:Alt>
</dc:title>
x-default 是没有指定首选语言时的默认项;如果有,它应排在第一位。dc:description 也能照这个办法保存多语言说明。不过老 SWF 里也常见更朴素的 <dc:title>Space Game</dc:title>。人眼读起来一样轻松,程序却不能假定每个 packet 都长成同一种样子。
dc:creator:有顺序的创作者列表
dc:creator 则是一份有顺序的名单,通常用 rdf:Seq 表达;较主要的创作者会排在前面:
<dc:creator>
<rdf:Seq>
<rdf:li>Jane Doe</rdf:li>
<rdf:li>Example Studio</rdf:li>
</rdf:Seq>
</dc:creator>
这几种 RDF 容器各有脾气:rdf:Seq 看顺序,rdf:Bag 不看,rdf:Alt 放备选值,多语言文本还要看 xml:lang。如果把它们统统剥成一行字,名单和语言信息也就一起被压平了。
SWF Metadata(Tag 77)
在 SWF 里,整包 XMP 被塞进可选的 Tag 77。这个标签的结构出奇地简单:
RECORDHEADER Tag 类型与 Tag body 长度
Metadata STRING:XML / XMP 元数据
这里的 STRING 以空字节结束。按 SWF 19 的规则,一份文件最多放一个 Metadata 标签,内容应是符合 XMP 的 RDF。Flash Player 本身会把它略过去:它不改变舞台、时间轴、声音或脚本,原本就是留给搜索引擎等外部工具阅读的。
这也解释了为什么“文件里有 XMP”不等于“现代网页 SEO 自动变好”。搜索引擎先看到服务器输出的 HTML、标题、正文和结构化数据;只有网站主动把 packet 里的文字提出来,它才真正出现在网页上。本馆在服务端生成作品页时展示少量字段,做的正是这一步。
hasMetadata 只是门口的指示牌
SWF 8 以后,文件开头的 FileAttributes(Tag 69)有一个 HasMetadata 位。它像门口的指示牌,只告诉解析器“里面应该有 Metadata”:位为 1 时应有 Tag 77,位为 0 时不应有;反过来,只要出现 Tag 77,这个位也应当打开。
指示牌上没有 XML。真正的 packet 仍在 Tag 77,而归档里偶尔也会遇到截断文件或不太守规矩的制作工具,让两边对不上。因此本馆分别记下它们:读到 Tag 69 就记录 attrs.has_metadata,真正遇到 Tag 77 才建立 xmp 并设置 xmp.present = true。
提取器不会因为指示牌亮着就虚构一份 XMP,也不会强行把两者改成一致。异常文件若塞进多个 Tag 77,标签直方图会照实计数,内存里的 xmp 则保留最后读到的那份;这是遇到怪文件时的容错方式,不是规范鼓励的写法。
本馆怎样提取三个字段
提取器先解开 FWS、CWS 或 ZWS 外层,再从头读过顶层 Tag。碰到 Tag 77,它会取出完整 body,在第一个空字节处停下,然后按 UTF-8 解码。此时内存里暂时有这些信息:
xmp.present
xmp.raw
xmp.dc_title
xmp.dc_creator
xmp.dc_description
三个 dc_ 字段走的是一条很轻的捷径,并没有搬来完整 XML/RDF 解析器。xmpField() 寻找字面量 <dc:title、<dc:creator 或 <dc:description,截取第一组开始与结束标签之间的内容,剥掉里面的标签,再整理空白。对大量 Adobe/Flex 生成的常见 packet,这样很快就能捞出适合列表页阅读的文字;有些 XML 虽不完整,只要外形还在,也可能被它救回来。
代价也很直白:换一个命名空间前缀就可能认不出,XML 实体、CDATA、属性式 RDF 和 XMP 别名也不在它的理解范围。rdf:Alt、rdf:Seq、xml:lang 被剥掉以后,多语言标题会挤成一段;多个 creator 仍按文本出现次序挤在一起,却失去了各项边界,也看不出原来是一组有序数组。
因此作品页展示的是“从 packet 里捞出的可读文字”,不是完整、规范化的 Dublin Core 结果。要做语言选择、权利审计、命名空间查询或往返编辑,最好还是从原 SWF 重新取出 packet,再交给真正的 XML/XMP 工具。
为什么不一定能查看原始 xmp.xml
刚才那份 xmp.raw 只在完整提取记录里短暂停留。数据进入当前 v3 管道时,还要经过两次瘦身:
compactMeta()丢掉xmp.raw,留下present和三个dc_展示字段;compactIntrinsicMetadata()再移除has_sidecar,因为当前 Worker 只向 D1 写紧凑记录,并不会另外创建 sidecar 对象。
因此,v3 里的 xmp.present = true 只说明提取器遇到了 Tag 77,不代表 R2 中一定有 {sha256}.swf/xmp.xml。当前作品详情也把“原始 XMP 可用”设为 false,所以不会显示“查看原始 xmp.xml”入口。
代码里仍保留 /flash/:id/xmp 查看页。它会直接去 R2 找这个对象键:找到就展示并提供下载,找不到便返回 404。这是给已经写入的历史 sidecar 留的通道;当前 v3 入库与元数据派生不保证为每个含 XMP 的 SWF 生成一份。
不过,原始 packet 并没有从档案里消失。Tag 77 仍躺在 SWF 字节中,下载原件后随时可以重新解析;缺的是方便网页直接打开的派生副本,不是原件里的数据。
本地文件播放器走的是另一条路,它可以直接使用尚未紧凑化的内存结果,所以有时能在页面里展开 xmp.raw。这并不表示归档 v3 详情接口也保存了同一份原文。
在作品页上怎么看
看到 dc:title、dc:creator 或 dc:description,就把它当作文件主动留下的一条线索。标题可能没有正确挑中当前语言,creator 可能是一串被压平的名单,说明文字也可能并不完整。反过来,三个字段都没出现,也不等于文件绝无 XMP:packet 也许只写了其他属性,或用了轻量扫描器不认识的 XML 写法。
同样,attrs.has_metadata = true 只是声明位亮着,xmp.present = true 才表示真的读到了 Tag 77;后者也不承诺网页端一定有原始 sidecar 可看。
XMP 的魅力正在这里:它可能把一段制作时代的自述一路带到今天,为标题、背景和检索补上一块拼图。真正整理归档事实时,再把它与来源页面、抓取时间、文件字节、制作工具和其他 SWF 结构放在一起看,会更踏实。