☰
Remotely Save 大文件忽略机制深度解析:阈值规则、加密尺寸比较与同步拒绝策略
2026/9/26 22:56:02 网站建设 项目流程
  • 数据同步

【免费下载链接】remotely-save

Sync notes between local and cloud with smart conflict: S3 (Amazon S3/Cloudflare R2/Backblaze B2/...), Dropbox, webdav (NextCloud/InfiniCLOUD/Synology/...), OneDrive, Google Drive (GDrive), Box, pCloud, Yandex Disk, Koofr, Azure Blob Storage.

项目地址:https://gitcode.com/gh_mirrors/re/remotely-save
点击查看免费下载

本文是 Remotely Save 同步算法系列文档中关于「忽略大文件」功能的专项技术指南。文章以 docs/sync_algorithm/sync_ignoring_large_files.md 为骨架,结合仓库源码(src/settings.ts、pro/src/sync.ts、src/fsEncrypt.ts 等)深入剖析阈值配置方式、E2E 加密下尺寸比较的特殊规则、文件“跨越阈值线”时的拒绝行为,以及各类同步场景下的判定分支,帮助你完整理解并安全使用 Skip Large Files 功能。

一、功能背景:从“不忽略”到“可忽略”

Remotely Save 插件在最初的设计中并不忽略任何大文件——所有文件(无论体积多大)都会参与本地与云端之间的同步。这虽然保证了数据的完整性,但会带来两方面问题:

  • 某些远程服务对单文件大小存在上传限制,超大文件会导致同步失败;
  • 用户可能希望将体积巨大的媒体文件(如视频、音频、压缩包)排除在同步范围之外,只同步笔记相关的文本类文件。

从 2022 年 5 月发布的新版本起,插件提供了Skip Large Files(跳过大文件)功能:可以设置一个大小阈值,让所有尺寸超过该阈值的文件不再参与同步。但由于该功能是在已有同步体系之上叠加的,插件必须制定一套与既有同步条件兼容的规则,避免破坏已同步文件的既有状态,这套规则正是本文的核心内容。

二、阈值配置:设置项与默认值

2.1 设置界面入口

在插件的「基础设置(Basic)」分区中,有一个名为Skip Large Files的下拉菜单,其描述文字为:

“Skip files with sizes larger than the threshold. Here 1 MB = 10^6 bytes.”(跳过大于某个阈值的文件。这里 1 MB = 10⁶ 字节。)

对应的设置实现位于 src/settings.ts:

new Setting(basicDiv) .setName(t("settings_skiplargefiles")) .setDesc(t("settings_skiplargefiles_desc")) .addDropdown((dropdown) => { dropdown.addOption("-1", t("settings_skiplargefiles_notset")); const mbs = [1, 5, 10, 20, 50, 100, 200, 500, 1000]; for (const mb of mbs) { dropdown.addOption(`${mb * 1000 * 1000}`, `${mb} MB`); } dropdown .setValue(`${this.plugin.settings.skipSizeLargerThan}`) .onChange(async (val) => { this.plugin.settings.skipSizeLargerThan = Number.parseInt(val); await this.plugin.saveSettings(); }); });

从源码可以看出关键信息:

项目说明
默认值-1,对应下拉菜单中的(not set)(不设置),即默认不忽略任何大文件
可选档位1、5、10、20、50、100、200、500、1000 MB
单位换算1 MB = 10⁶ 字节(1,000,000 字节),而非二进制 1024×1024,设置描述中已明确说明
存储字段settings.skipSizeLargerThan,以字节数为单位保存在插件配置中

该配置字段在 src/baseTypes.ts 中声明为可选数字skipSizeLargerThan?: number;,其默认值-1定义在 src/main.ts 的默认配置对象中:

skipSizeLargerThan: -1,

2.2 阈值语义:<=判定

需要特别注意的是,同步逻辑中对阈值的判断使用的是<=(小于等于)语义。以 pro/src/sync.ts 为例:

if ( skipSizeLargerThan <= 0 || remote.sizeEnc! <= skipSizeLargerThan ) {

这里skipSizeLargerThan <= 0相当于“未设置阈值”的短路开关:只要阈值小于等于 0(默认-1),所有文件都放行;只有文件尺寸大于阈值时才会被忽略,而尺寸恰好等于阈值的文件仍然正常同步。

三、核心规则:E2E 加密模式下比较“加密后尺寸”

规则的第一条,也是整个功能最容易产生误解的地方:如果用户启用了 E2E(端到端)加密密码模式,则文件大小的比较对象是“加密后的尺寸(encrypted sizes)”,而不是原始未加密的文件大小。

3.1 为什么要比较加密尺寸?

原文档给出了两点理由:

  1. 加密后的文件才是真正参与传输的对象——本地与远程之间实际传输的是加密后的字节流,远程服务上的文件大小记录的就是加密尺寸;
  2. 加密尺寸可以从明文尺寸单向推导,反向却不可行——从明文大小可以精确计算加密后的大小,但从加密大小无法唯一反推出明文大小(因为加密过程存在填充,同一加密尺寸可能对应一段明文尺寸区间)。

因此,以加密尺寸作为统一比较基准,才能保证本地与远程两侧的判定口径一致。

3.2 源码中的实现

在 src/fsEncrypt.ts 的encryptEntity方法中,当实体尚未填充sizeEnc时,会从原始尺寸换算加密尺寸:

const local = cloneDeep(input); if (local.sizeEnc === undefined && local.size !== undefined) { // it's not filled yet, we fill it local.sizeEnc = this._getSizeFromOrigToEnc(local.size); }

换算方法_getSizeFromOrigToEnc在 src/fsEncrypt.ts 中按加密算法分派:

_getSizeFromOrigToEnc(x: number) { if (this.password === "") { return x; } if (this.method === "openssl-base64") { return openssl.getSizeFromOrigToEnc(x); } else if (this.method === "rclone-base64") { return rclone.getSizeFromOrigToEnc(x); } else { throw Error(`not supported encrypt method=${this.method}`); } }

而实体统一数据结构中的sizeEnc字段定义在 src/baseTypes.ts:

size?: number; // might be unknown or to be filled sizeEnc?: number; sizeRaw: number;

3.3 两种加密算法的尺寸换算细节

OpenSSL 加密(openssl-base64)的换算实现在 src/encryptOpenSSL.ts:

export const getSizeFromOrigToEnc = (x: number) => { if (x < 0 || Number.isNaN(x) || !Number.isInteger(x)) { throw Error(`getSizeFromOrigToEnc: x=${x} is not a valid size`); } return (Math.floor(x / 16) + 1) * 16 + 16; }; export const getSizeFromEncToOrig = (x: number) => { if (x < 32 || Number.isNaN(x) || !Number.isInteger(x)) { throw Error(`getSizeFromEncToOrig: ${x} is not a valid size`); } if (x % 16 !== 0) { throw Error( `getSizeFromEncToOrig: ${x} is not a valid encrypted file size` ); } return { minSize: ((x - 16) / 16 - 1) * 16, maxSize: ((x - 16) / 16 - 1) * 16 + 15, }; };

可以看到:每个明文文件加密后,至少增加 16 字节的 salt 与 16 字节的填充(以 16 字节块对齐)。加密尺寸公式为(⌊x/16⌋ + 1) × 16 + 16,反向换算只能得到一个 15 字节宽的明文尺寸区间,这正是“加密尺寸可由明文尺寸计算、反向却不可行”的数学基础。测试用例 tests/encryptOpenSSL.test.ts 验证了这一换算关系:

assert.equal(getSizeFromOrigToEnc(0), 32); assert.equal(getSizeFromOrigToEnc(15), 32); assert.equal(getSizeFromOrigToEnc(16), 48); assert.equal(getSizeFromOrigToEnc(31), 48); assert.equal(getSizeFromOrigToEnc(32), 64); assert.equal(getSizeFromOrigToEnc(14787203), 14787232);

而rclone 加密(rclone-base64)则直接复用@fyears/rclone-crypt包的encryptedSize函数,见 src/encryptRClone.ts:

import { Cipher as CipherRCloneCryptPack, encryptedSize, } from "@fyears/rclone-crypt"; export const getSizeFromOrigToEnc = encryptedSize;

3.4 加密尺寸的实测佐证

仓库测试资源 tests/static_assets/mona_lisa 提供了真实的加解密样本:原始图片1374px-Mona_Lisa,..._retouched.jpg大小为 1,141,550 字节,其加密产物1374px-Mona_Lisa,..._retouched.jpg.enc大小为 1,141,568 字节,恰好相差 18 字节(16 字节 salt + 2 字节块内填充对齐),与上述公式吻合。生成加密文件的命令记录在 tests/static_assets/mona_lisa/openssl_command.sh:

openssl enc -p -aes-256-cbc -S 8302F586FAB491EC -pbkdf2 -iter 10000 -pass pass:somepassword -in '1374px-Mona_Lisa,_by_Leonardo_da_Vinci,_from_C2RMF_retouched.jpg' -out 1374px-Mona_Lisa,_by_Leonardo_da_Vinci,_from_C2RMF_retouched.jpg.enc

3.5 加密模式下的实际阈值设定技巧

由于比较的是加密尺寸,用户在 E2E 加密模式下设置阈值时需要留意:实际被拦截的明文文件大小会略小于阈值。例如设置阈值 1,000,000 字节时,一个明文大小恰为 999,990 字节的文件加密后会膨胀到约 1,000,00x 字节,从而被判定为“超过阈值”而忽略。若你的目标是“忽略明文大于某值的文件”,应适当调高阈值档位以留出加密膨胀的余量。

四、四象限判定:文件“跨越阈值线”时拒绝同步

规则的第二条针对的是已在本地与远程之间完成同步的文件 A。插件按本地尺寸与远程尺寸相对于阈值的分布,分四种情况处理:

本地尺寸远程尺寸插件行为
低于阈值低于阈值正常同步
高于阈值高于阈值正常忽略
低于阈值高于阈值拒绝同步,并向用户抛出错误
高于阈值低于阈值拒绝同步,并向用户抛出错误

其中第 3、4 种情况就是文档强调的核心观点——当文件大小“跨越了阈值线”(即两侧对同一文件是否应忽略的判断不一致)时,插件不会自作主张引入更多麻烦(如单侧删除或强制覆盖),而是直接拒绝处理该文件并抛出错误,把决策权交还给用户。

需要注意的是:该规则同样适用于删除操作。即当文件已同步、之后某一侧发生删除时,也会应用相同的“跨越阈值线则拒绝”的判定,避免在忽略大文件的状态下误删另一侧的副本。

4.1 源码中的拒绝行为实现

上述“拒绝并抛错”的行为在 pro/src/sync.ts 中体现为:当单侧发生修改/删除、且该侧尺寸超过阈值时,直接throw Error。例如 pro/src/sync.ts 中“本地未变、远程被修改”的分支(branch 9):

if (localEqualPrevSync && !remoteEqualPrevSync) { // If only one compares true (no prev also means it compares False), the other is modified. Backup and sync. if ( skipSizeLargerThan <= 0 || remote.sizeEnc! <= skipSizeLargerThan ) { // ... 正常执行 remote_is_modified_then_pull } else { throw Error( `remote is modified (branch 9) but size larger than ${skipSizeLargerThan}, don't know what to do: ${JSON.stringify( mixedEntry )}` ); } }

同理还有“远程未变、本地被修改”(branch 10,pro/src/sync.ts)、“远程被删除但本地修改”(branch 5,pro/src/sync.ts)、“本地被删除但远程修改”(branch 8,pro/src/sync.ts)等多个分支,均在尺寸超限时抛出形如... but size larger than ${skipSizeLargerThan}, don't know what to do的错误,并在错误信息中包含完整的实体数据(JSON.stringify(mixedEntry)),方便用户定位是哪个文件出了问题。

4.2 新文件场景:正常忽略

与“跨越阈值线”的拒绝不同,对于全新出现(不在上一次同步记录中)的单侧文件,插件会直接静默忽略而不抛错。例如 pro/src/sync.ts 中“本地新建超大文件”的分支:

if (prevSync === undefined) { // if A is not in the previous list, A is new if (skipSizeLargerThan <= 0 || local.sizeEnc! <= skipSizeLargerThan) { // ... 正常执行 local_is_created_then_push } else { mixedEntry.decisionBranch = 37; mixedEntry.decision = "local_is_created_too_large_then_do_nothing"; mixedEntry.change = false; keptFolder.add(getParentFolder(key)); } }

这里决策分支 37(local_is_created_too_large_then_do_nothing)意味着:新建文件如果超过阈值,直接标记为“什么都不做”(不推送、不记录),文件保留在本地但不会同步到远程。远程新建超大文件的处理逻辑与之对称(见 pro/src/sync.ts 的 branch 28 场景)。这是四象限规则之外最常用到的行为——大多数用户设置 Skip Large Files 的初衷,正是让新增的大文件安静地留在本地。

五、规则总结与实践建议

5.1 规则要点速览

  1. 默认不忽略任何文件(skipSizeLargerThan = -1),需在设置中显式选择阈值档位(1~1000 MB,1 MB = 10⁶ 字节);
  2. E2E 加密模式下,比较的是加密后尺寸,且加密尺寸由明文尺寸单向可算、反向不可算;
  3. 两侧尺寸同为“低于阈值”则正常同步,同为“高于阈值”则正常忽略;
  4. 两侧判定不一致(跨越阈值线)时拒绝同步并抛错,删除场景同样适用;
  5. 全新单侧大文件(无同步历史)会被静默忽略,不抛错、不传输。

5.2 实践建议

  • 启用前先评估现有数据:如果你已经同步过一批大文件,再开启本功能,那些“跨线”文件会导致同步报错。建议先在设置中把阈值调整到覆盖所有既有文件之上(或先手动处理掉超限文件),再开启忽略功能;
  • 加密用户注意膨胀余量:由于比较的是加密尺寸,请为阈值预留约 2 KB 以上的加密膨胀空间(具体取决于文件大小与加密算法);
  • 报错即人工介入的信号:当同步日志中出现but size larger than ... don't know what to do错误时,意味着某个已同步文件跨越了阈值线,需要你手动决定保留哪一侧,然后重新同步;
  • 新增大文件默认不报错:如果你只是想“以后的大文件都别同步”,直接设置阈值即可,新建的超大文件会被静默忽略,不会打断同步流程。

六、延伸阅读

  • 同步算法的整体设计文档:docs/sync_algorithm/README.md(含 v1/v2/v3 三个版本的算法演进说明,分别见 docs/sync_algorithm/v1/README.md、docs/sync_algorithm/v2/README.md、docs/sync_algorithm/v3/README.md);
  • 同步决策分支与 mixed entity 的数据结构定义:src/baseTypes.ts;
  • 加密尺寸换算的完整单元测试:tests/encryptOpenSSL.test.ts;
  • 加密整体机制说明:docs/encryption/README.md 与 docs/encryption/openssl.md。
  • 数据同步

【免费下载链接】remotely-save

Sync notes between local and cloud with smart conflict: S3 (Amazon S3/Cloudflare R2/Backblaze B2/...), Dropbox, webdav (NextCloud/InfiniCLOUD/Synology/...), OneDrive, Google Drive (GDrive), Box, pCloud, Yandex Disk, Koofr, Azure Blob Storage.

项目地址:https://gitcode.com/gh_mirrors/re/remotely-save
点击查看免费下载

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询