首页
/ Strapi Data Transfer 数据文件(.tar)内部结构详解:目录布局、JSONL 分片与加密压缩机制

Strapi Data Transfer 数据文件(.tar)内部结构详解:目录布局、JSONL 分片与加密压缩机制

2026-09-04 15:22:29作者:房伟宁

Strapi 的数据迁移(Data Transfer)模块定义了标准的 Strapi Data File:一个以 .tar 为基础、可选 gzip 压缩和 AES 加密的归档文件,内部用 POSIX 风格路径组织 metadata.json 与各数据阶段的 JSONL 分片。读完本文,你将理解该数据文件的完整目录结构、metadata.json 的兼容字段、{stage}_{序号}.jsonl 的连续编号读取约定,以及 JSON Lines 流式解析为何能让大规模传输保持低内存占用,并能对照 Strapi File Structure 官方文档 在仓库源码中验证每一处约定。

数据文件的载体:tar + 可选 gzip + 可选加密

根据文档定义,Strapi 的文件 Provider(File Provider)期望的输入是一个 .tar 文件,它可以:

  • 可选地被 gzip 压缩;
  • 可选地用 aes-128-ecb 算法加密。

归档内部的路径一律使用 POSIX 风格(正斜杠分隔)。这一点在目标端 Provider 的落盘逻辑中得到了印证:最终产物的文件名就是 路径.tar,开启压缩后追加 .gz 后缀,再开启加密后追加 .enc 后缀,三段式扩展名顺序固定为 .tar.gz.enc,见 归档路径拼接逻辑

get #archivePath() {
  let filePath = `${file.path}.tar`;
  if (compression.enabled) {
    filePath += '.gz';
  }
  if (encryption.enabled) {
    filePath += '.enc';
  }
  return filePath;
}

在流管线中,加密与压缩的顺序是:tar.pack() 产出原始归档字节流 → 先经过 gzip → 再经过加密密码器,写入磁盘。该管线组装见 bootstrap 中的 archiveTransforms。源端读取时的反序管线(fs.createReadStream → 解密 → gunzip)则在 源端 Provider 的 #getBackupStream 中实现。

加密算法的默认值正是文档所说的 aes-128-ecbcreateEncryptionCipher 的第二个参数默认 'aes-128-ecb'。值得注意的实现细节是,密钥并非直接用作密码,而是先经 scryptSync(key, '', 16) 派生出 16 字节安全密钥再交给 createCipheriv,同一文件里还定义了 aes128/aes192/aes256 等其他策略(见 encrypt.ts)。

顶层目录结构:metadata 与四个数据阶段

文档给出的标准布局如下(此处完整继承原文档的结构树):

./
configuration
entities
links
metadata.json
schemas

./configuration:
configuration_00001.jsonl

./entities:
entities_00001.jsonl

./links:
links_00001.jsonl

./schemas:
schemas_00001.jsonl

即根目录下有一个 metadata.json 文件,加上 configurationentitieslinksschemas 四个目录,每个目录内放按序号编号的 .jsonl 文件。

从源码结构看,这四个阶段与源端 Provider 的一对读流方法一一对应:createEntitiesReadStream / createSchemasReadStream / createLinksReadStream / createConfigurationReadStream 分别调用 #streamJsonlDirectory('entities' | 'schemas' | 'links' | 'configuration'),以目录名为过滤条件从 tar 归档中筛选出对应 JSONL 文件逐个流式解析。也就是说,目录名即数据阶段(stage)的标识,Provider 不依赖任何清单文件,只靠路径前缀来归类。

此外,归档中还会携带二进制资产:媒体文件放在 assets/uploads/,其对应的 JSON 元数据放在 assets/metadata/{filename}.json。源端 createAssetsReadStream 用 tar 的 Parser 过滤 assets/uploads 下的文件条目,并逐一读取同名元数据组装成 IAsset 对象;目标端 createAssetsWriteStream 则反向写入这两条路径。资产是 tar 的原生文件条目而非 JSONL,这一点与四个文本数据阶段有本质区别。

metadata.json:来源信息与版本兼容检查

文档要求 metadata.json 至少包含:

  • createdAt:文件创建时间戳;
  • strapi.version:创建该文件的 Strapi 版本(用于兼容性检查)。

示例(继承自原文档):

{
  "createdAt": "2023-06-26T07:31:20.062Z",
  "strapi": {
    "version": "4.11.2"
  }
}

该结构在类型层有精确对应——IMetadatacreatedAtstrapi.version 都定义为可选字段:

export interface IMetadata {
  strapi?: {
    version?: string;
  };

  createdAt?: string;
}

源端 Provider 把 metadata.json 作为文件有效性的前置探针bootstrap 阶段会先尝试解析该文件,一旦失败就抛出 ProviderInitializationError,且当文件处于加密状态时会提示“Key is incorrect or the file ... is not a valid Strapi data file.”——这实际上把“密钥错误”和“文件损坏”合并为同一诊断入口。getMetadata() 返回的元数据按 IProvider 接口的注释 用途是 version validation(版本校验),即迁移引擎据此做跨版本兼容判断。

目标端在收尾时会把源端元数据原样写回归档:LocalFileDestinationProvider.close 调用 #writeMetadata,通过 createTarEntryStream 写入名为 metadata.json 的 tar 条目。因此一份再导出的备份会保留源文件的版本与时间戳信息。

JSONL 分片:命名格式与“连续编号读到缺失即结束”的约定

文档对每个数据阶段目录的约定是:

  1. 文件名格式为 {stage}/{stage}_{5位序号}.jsonl,例如 entities/entities_00001.jsonl
  2. 每个阶段可以有任意数量的文件,只要序号连续;
  3. 读取规则:读完 00001 后尝试读取 00002,若不存在,则认为该阶段数据读取完毕。

写入端的命名规则由 createFilePathFactory 固化,padStart(5, '0') 正好对应“5 位序号”:

export const createFilePathFactory =
  (type: string) =>
  (fileIndex = 0): string => {
    // always write tar files with posix paths so we have a standard format for paths regardless of system
    return posix.join(
      // "{type}" directory
      type,
      // "${type}_XXXXX.jsonl" file
      `${type}_${String(fileIndex).padStart(5, '0')}.jsonl`
    );
  };

注意注释明确说明:tar 内路径总是写成 POSIX 格式,与操作系统无关。

至于“什么时候切一个新分片”,createTarEntryStream 给出了答案:写入缓冲累计超过 maxSizeflush() 一个 tar 条目并使 fileIndex 自增。maxSize 的默认值是 2.56e8(约 256 MB),而 Provider 选项中的 file.maxSizeJsonl 允许调用方覆盖它——这对应 ILocalFileDestinationProviderOptions 中定义的 maxSize / maxSizeJsonl 参数(见 Destination 选项文档)。单个 chunk 本身超过上限时会直接抛出 payload too large 错误,防止单个 JSON 对象撑爆一个分片。

读取端并不用“先列目录再找最后一个文件”的方式,而是由 tar 的 Parser 按归档内实际出现的所有匹配条目依次触发 onReadEntry——由于 tar 条目顺序即写入顺序(00001、00002、…),文档描述的“读到缺失即完成”在流式读取下天然成立,无需随机访问。

为什么是 JSON Lines:逐行解析与恒定内存占用

原文档给出的理由是:JSON Lines(.jsonl)文件以换行符分隔 JSON 对象,使 Provider 可以一次只读一行,而不是把整个文件载入内存,从而最小化传输过程中的 RAM 占用,并允许单个文件包含任意多的数据。

源码中这一设计体现在 源端 #streamJsonlDirectory:每个 tar 条目(即一个 JSONL 分片)被接上 stream-jsonjsonl/Parser(开启 checkErrors: true),逐行产出对象后再转成统一的下行流;写入端则对称地使用 stream-json/jsonl/Stringer 将对象逐行序列化(见 createEntitiesWriteStream 中的 chain([stringer(), entryStream]))。整条链路是纯流式的:tar 解包 → 逐行 JSON 解析 → 逐条写入,任何时刻内存中只有“一个 tar 条目的一行 + 少量缓冲”。

这个流式行为有专门的测试佐证,例如 源端 Provider 测试 中构造真实 tar 归档并验证 entities 读流在下游消费慢时能正确暂停(backpressure),说明大文件传输不会因缓冲失控。

另外有一个容易被忽略的兼容细节:分片解析失败时不会静默跳过,而是销毁输出流并抛出带上下文的 ProviderTransferError(指明是哪个备份文件解析出错,见 异常处理),便于用户定位损坏的分片。

POSIX 路径约定与旧版 Windows 路径的兼容

文档强调归档内部必须使用 POSIX 风格路径。源端的 路径工具函数 解释了为什么还需要额外的容错代码:在迁移引擎 4.9.0 及更早版本中,Windows 系统上生成的导出文件里混有 Windows 风格反斜杠路径;现在所有路径统一存为 POSIX,但为了兼容遗留文件和用户在 Windows 上用第三方 tar 工具手工制作的备份,读取时需要做分隔符归一化:

  • unknownPathToPosix:若路径不含正斜杠则按 win32 反斜杠转换为 POSIX;
  • isPathEquivalent:判断两条未知格式路径归一化后是否指向同一位置(用于精确匹配 metadata.json 这类单文件);
  • isFilePathInDirname:判断某文件是否落在指定阶段目录内(用于 JSONL 分片过滤)。

该文件头部注释还说明了一个约束:由于分隔符转换的存在,导出文件中的文件名永远不能包含正斜杠(即使转义也不行),这是手工制作 tar 备份时需要遵守的格式限制。

小结:从文档约定到源码实现的一一映射

文档约定 源码落点
.tar + 可选 gzip + 可选 aes-128-ecb 归档路径与管线加密策略
内部使用 POSIX 路径 路径归一化工具createFilePathFactoryposix.join
metadata.jsoncreatedAtstrapi.version IMetadata 类型bootstrap 前置校验
{stage}_{5位序号}.jsonl 命名 createFilePathFactory
每阶段任意数量分片、读到缺失即结束 源端流式读取
JSONL 逐行解析、低内存传输 stream-jsonjsonl/Parser / Stringer(源端与目标端读写流)

配合 Source Provider 文档Destination Provider 文档 可以进一步看到:CLI 导入文件时会根据扩展名(.gz.enc)自动推断压缩/加密选项,而 source::local-filedestination::local-file 两个 Provider 正是本文所讲文件结构的生产者与消费者。理解这套布局后,你就能手工检查一份 Strapi Data File 的内部构成,或在自定义工具中按同一约定生成可被 Strapi 迁移引擎识别的备份文件。

登录后查看全文
热门项目推荐
相关项目推荐

项目优选

收起
kernelkernel
deepin linux kernel
C
33
18
ops-transformerops-transformer
本项目是CANN提供的transformer类大模型算子库,实现网络在NPU上加速计算。
C++
1.12 K
2.72 K
kernelkernel
openEuler内核是openEuler操作系统的核心,既是系统性能与稳定性的基石,也是连接处理器、设备与服务的桥梁。
C
527
590
ops-nnops-nn
本项目是CANN提供的神经网络类计算算子库,实现网络在NPU上加速计算。
C++
904
1.82 K
pytorchpytorch
作为 Ascend for PyTorch 社区的核心组件,TorchNPU 是昇腾专为 PyTorch 打造的深度学习适配插件,使 PyTorch 框架能够直接调用昇腾 NPU,为开发者提供昇腾 AI 处理器的超强算力。
Python
854
1.34 K
docsdocs
暂无描述
Markdown
889
5.78 K
jiuwenswarmjiuwenswarm
JiuwenSwarm 是一款基于openJiuwen开发的智能AI Agent,它能够将大语言模型的强大能力,通过你日常使用的各类通讯应用,直接延伸至你的指尖。
Python
3.52 K
1.01 K
ops-mathops-math
本项目是CANN提供的数学类基础计算算子库,实现网络在NPU上加速计算。
C++
1.33 K
1.45 K
cann-learning-hubcann-learning-hub
CANN 学习中心仓,支持在线互动运行、边学边练,提供教程、示例与优化方案,一站式助力昇腾开发者快速上手。
Jupyter Notebook
980
502
AscendNPU-IRAscendNPU-IR
AscendNPU-IR是基于MLIR(Multi-Level Intermediate Representation)构建的,面向昇腾亲和算子编译时使用的中间表示,提供昇腾完备表达能力,通过编译优化提升昇腾AI处理器计算效率,支持通过生态框架使能昇腾AI处理器与深度调优
C++
540
384