使用Windows Task Scheduler进行OneDrive强制同步

发布时间 2023-09-11 20:59:25作者: 橙子和雪

前言

OneDrive的同步策略非常反人类:它允许用户同步文件,但仅限于其划定范围的特定文件夹/文件类型。这意味着用户不能对任意文件夹进行同步,简直是难以想象!

图1 OneDrive对备份文件的选项仅限于几个文件夹内,体现了老牌科技企业在教育用户如何使用计算机上的良苦用心

Strawman Solution

我们可以使用链接(在Windows的语境下似乎被称为junction)实现同步任意文件夹,即在OneDrive中创建指向本地文件的链接,以实现文件的同步。
具体地,使用如下命令:

mklink /j C:\Users\username\Onedrive\folder C:\path\to\any\folder

但遗憾的是,在使用这种方法时,OneDrive已经不能正确地实现对文件变更的及时同步了,详见这个链接
简单来说,这是因为文件系统没有通知OneDrive,所以本地磁盘上做出的更改不会被OneDrive及时发现,也并不会即时同步到云端。这些更改可能需要等上很久才能被OneDrive发现,而这显然与使用云盘备份文件的初衷不符:如果下一秒我的电脑就坏了呢?
(注:用户表示重启能够触发OneDrive的同步检查,但笔者的电脑基本上从不关机,也很少重启,所以这也不是一个令人满意的选择)

为了让OneDrive及时与文件系统同步,一个可能的解决方法是手动进行同步。可惜的是OneDrive仍然没有给出这样的接口,因为它的设计哲学是“用户把文件直接存入云端,在本地创建指向它的链接”。这可真是太厉害了,用户完全不会遇到没网而访问不了云盘的情况呢!用户永远不会遇到版本冲突而心惊胆战地不知道覆盖哪个版本的情况呢!
然而聪明的用户发现,如果OneDrive的根目录发生了文件修改,它就会自动对所有OneDrive内的文件进行一次检查,如果有未同步的更改,就会将这些修改进行同步。
基于此,出现了OneDrive Bully这样的工具,通过周期性地对一个冗余文件重命名,实现对文件系统更改的定期检查。

更干净的解决方案

然而笔者并不太喜欢这样的做法,因为这会让OneDrive的修改记录里全都是这样的记录,从而可能漏掉真正有意义的修改记录。
能不能想办法按需地进行强制同步呢?用户使用工作电脑时,一般不需要把文件同步到云端;而离开工作电脑时,才有把文件同步至云端,供其它计算机使用的情况(假设只有一个用户)。那么,只需在锁屏时进行强制同步就足够了
为了实现这个功能,使用Windows自带的Task Scheduler,将“用户锁屏”这个行为作为触发:

图2 “工作站锁定时”对应的就是用户的锁屏行为

触发时,执行下述脚本:

@echo off

set timestamp=%date:~0,4%%date:~5,2%%date:~8,2%%time:~0,2%%time:~3,2%%time:~6,2%

set odpath=C:\Users\%username%\OneDrive\

set syncprefix=SleepSync

set newfilename=%odpath%%syncprefix%_%timestamp%

if exist %odpath%%syncprefix%* (
	rem the syntax of batch script is so weird:
	rem `ren C:\path\to\oldfile ren C:\path\to\newfile` is illegal...
	rem I will have to do `ren absolute_path new_name`, how ugly!  
	ren %odpath%%syncprefix%* %syncprefix%_%timestamp%
) else (
	type NUL > %newfilename%
)

其实就是实现了OneDrive Bully的最小化功能。

总结

  1. 保证单向同步是防止出现数据版本冲突的关键。但是,当数据的原始版本存储在云端时,用户为了离线/低延迟访问,就不得不在本地做拷贝,从而违背这个原则。因此,我不认为把原始拷贝放在云端是一个明智的选择。
  2. Windows批处理脚本的语法很奇怪。
  3. Task Scheduler中的“用户会话断开连接”在使用Win+L进行锁屏时并不会触发。