这件事情要从初一开始讲起。
零. 遇见
2020年9月,我来到人翠就读。在这里,我发现了一个很有趣的现象:几乎所有老师来上课都不带U盘,讲课的课件似乎都是“凭空冒出来的”。这极大地激发了我的兴趣。
经过仔细观察,我发现那些课件并不是凭空出现的。老师们上课前会点开班级电脑的桌面上的一个名为“临时传递”的文件夹(别问我为什么名称这么奇怪,那上面就那么写的,而且老师们都这么叫),课件全都在那里。准确的来说,从图标上看,那不完全是个文件夹,它的图标左下角有一抹绿,后来我得知,那是因为“临时传递”是一个共享文件夹。

简单说来,在我们学校有一台仿佛“服务器”的这么一台电脑,它存储了全校所有老师需要的文件,并且可以在整个局域网内部的任意一台电脑随意上传、访问、修改和删除其中文件。也就是说,它充当着一个“局域网U盘”的角色。
局域网内部共享文件这个功能是Windows自带的,根据我的测试,最晚从Windows 7就支持此功能了。在“服务端”设备(即,实际存储着这些文件的电脑)上,我们可以在“文件资源管理器”中右键文件夹(或者磁盘),点击“共享…”(注:在不同版本的Windows中这个选项的名称不完全一致),接下来系统会弹出弹窗要求你设置访问权限等。我校的“临时传递”的权限应当是设置的“Everyone”均可以读取/写入。至于在“客户端”设备(即,直接使用这些文件的电脑)上,应当首先确认与“服务端”连在了同一个局域网内。接下来在任意“文件资源管理器”目录内空白处或者桌面上右键,新建快捷方式,将快捷方式指向共享文件夹的UNC路径即可。例如,根据我的观察,我发现我校的“临时传递”的路径是“\\172.17.17.17\临时传递\”。
其实这个路径并不难找。由于快捷方式指向的就是它,所以每次打开该快捷方式之后,文件资源管理器上面路径栏就会暴露出真实的路径,就像你在你的电脑上的“磁盘:\路径”一样。
初一的时候我才疏学浅,当时看到了“服务端”IP是172.17.17.17,看见这么多个“17”,知道必是有意而为之,还想了很久“这么靓的IP岂不是很贵”,后来才知道,有“IP保留地址”这种东西,换句话说,这只是个内部网络的IP,这个IP地址几乎是可以随意设置的(只要设定得还符合规定)。
后来在一节信息课上,我成功在南楼二层机房连接上了临时传递。在另一节课上我还成功使机房的一台电脑作为共享“服务端”,做了一个“临时传递的孪生姊妹”,放了一些瞎写的文件。
不过直到如今,我一直不曾见到“临时传递”所在设备(以下亦称“Angelina”)的真面目。看起来它的存储真的很大,因为有一次我去看临时传递的属性已经有数百GB了,很显然,老师们并不打算把它当作一个“临时”传递,他们甚至愿意把几乎三年的课件都放在那里。不过,我倒是大概知道Angelina的地理位置在哪里。根据我的推断,它大概就在教学楼(北楼)四层西侧女厕所……
我开玩笑的。我是说,四层西侧女厕所对面的那个永远锁着门、神秘兮兮的屋子。临时传递的所在设备Angelina应该就在此处。
一. 心动
有时候,我们已出了校门,回到家,想起老师课上讲的一句什么,努力回忆却再也记不起来,书上也找不到,笔记上也没有,当时记笔记的时候自认为那句话不是重点。
亦或者,我们只是想要在校外借用一下临时传递上面的一些数据或者资源。
于是,我萌生了一个很宏大的理想:能不能让我们在公开网络上访问“临时传递”呢?
首先,Windows原生的共享文件夹肯定是不能直接通过Angelina公开到公网的。(不然早就有1320个病毒潜伏在临时传递文件夹里面了)
不过,我们可以借助一些中转。比如,这是我的第一个设想。

我们用一台在公网的服务器(称Daisy),与另一台在校内的客户机(称Cindy)进行通信。
比如,用户访问https://mirror-rdfzcw-inside-files-daisy.apps.czhiming.cn/2023级(初三)/2班/初三第二学期/语文/曹刿论战2023.pptx
,(本URL不是真实的,并不存在这个网站)则Daisy会去尝试将/2023级(初三)/2班/初三第二学期/语文/曹刿论战2023.pptx
发送给在校内的客户端Cindy,要求其获取本文件的内容。如果Cindy在线,它将会尝试连接到临时传递Angelina,打开该文件并传输给公网服务器,也就是Daisy。然后Daisy将获取到的文件传给用户。如果碰巧此时Cindy不在线,Daisy便会从其本地缓存中调取该文件(但是该文件可能不是最新版本的),发送给用户。
一切看起来都是那么美好而完美无瑕。但是实际上,它有很多很多的问题。比如说,当文件是从Cindy获取的时候,我们的请求将会是,用户->Daisy->Cindy->Angelina,而文件将会以相反的路途传回来。这显然会慢的要死(而且学校带宽慢的离谱,这更是雪上加霜)。而当文件是从Daisy的缓存获取的时候,请问,你觉得,Daisy要有一个多大的磁盘?我到哪儿去找一个这么大的磁盘来?😂
于是这个方案很显然行不通了。不久,经过我的持续思考,这是最终的设计:
Daisy获取文件和用户获取文件不同步发生。具体说来,我们可以设定Cindy定时与Daisy同步“临时传递”的数据,而用户访问时,直接从Daisy获取数据。而Daisy不再是一个普通的服务器,而是Microsoft 365 OneDrive(总储量5TB,上传/下载可达10MB/s)。


这样,我们的理论基础就基本上完成了,就差实践了。于是,在一个风和日丽的中午,我鬼鬼祟祟地打开了班里电脑上的Microsoft OneDrive客户端,登录我先前专门为本项目分配的账号,然后点选了一些选项,选择了一些要上传的文件夹,最小化了窗口,然后就下楼去食堂了。我回来的时候,从底部的状态栏看,它依然在同步文件。后来我才得知,它那天硬生生传了10GB上来😂,我都没预料到我选择的那几个文件夹那么大。
不过很快我发现微软官方的OneDrive客户端不太能满足我的需要了。这个软件本身是为用户提供一些简单的图片、视频、文档之类的同步功能的,设计意图与我的这个项目略有不同。比如说,如果老师在临时传递(Angelina)删除了一个之前已经被上传到Daisy的文件,下次同步后,该文件又会出现在临时传递里;再比如它可以选择上传多个文件夹,但是不能同时同步多个文件夹,这无疑增加了人工操作的负担;还有它对于很多非常规情况的处理方法和效果也不是我想要的。于是,只好——“自己动手,丰衣足食”。
二. 追求
所以我们现在要做一个OAuth App,主要功能就是上传(同步)文件到OneDrive,并且处理非常规情况时,有独特的逻辑。
为了访问到OneDrive,最简单可靠的方式是通过Microsoft Graph API。微软还是很不错的,提供了很详细的说明文档。不过,这个文档似乎是面向有经验的程序员们的,因此它省略了很多,我这种初次接触OAuth的人并不能完全看懂。😅
它倒是还给了示例代码。不过,它给的Python示例代码(是的,我打算用Python实现这个程序)是用的Flask框架,目测是要做个网站,可以通过微软API第三方登录什么的。但是我要做的不是网站,就是一个简简单单的在后台跑的小软件而已好吧,所以这个示例代码对我来说也没用了。(我没接触过Flask,导致示例代码看起来像天书,我也不知道哪些代码是Flask的、哪些是Microsoft登录的)
于是我只好自己去百度。在知乎上我找到了一篇讲解得更能让我理解的文章,于是我按照它的指引开始了此项目程序的开发。
首先,显然我们要去Azure的Azure AD注册一个新的应用,记下它的client id,然后去“令牌与密码”选项卡新建一个client secret。并且在“API权限”选项卡添加诸如files.read、files.readwrite、offline_access等Microsoft Graph API的权限,并点击“代表组织同意”按钮。接着我们还要配置数据回传URL,我配置的就是http://localhost
。
接下来差不多可以开始登录账号了。首先在此之前我们要先了解一下它的登录原理。我只是看懂了,但是并不完全看懂,而且不知道它为啥非要这么复杂。对于我这种开发的登录都只会账号密码明文存储明文传输的人来说,看微软的登录,简直就是刘姥姥进了大观园。
首先,我们在浏览器上访问一个URL,其中参数指定了Oauth App的client id。它会跳到登录,要求用户登录账号,并且登录成功之后会提示“是否允许此OAuth App访问你的这些数据”这种安全提示。允许之后,会跳到之前我们设定的URL,其中的URL参数包括一个Authorization Code。接着,我们POST另一个URL,带着我们的这个Authorization Code,还有Oauth App的client id和client secret。接着,它会返回给我们一个access_token和一个refresh_token。access_token是真正用API的时候拿来用的,但是它有有效期,大约1个小时。过去以后,要用refresh_token去另外一个地方兑换新的access_token。
(真的就离谱😂)
import requests # to send http requests
import json # to process data
client_id = ''
client_secret = ''
login_url = "https://login.microsoftonline.com/common/oauth2/v2.0/authorize?client_id="+ client_id + "&scope=files.readwrite files.read offline_access&redirect_uri=http://localhost&response_type=code"
print("Open this link - \n"+login_url)
code = input("\nAnd input the 'code' parameter inside here.\n"
data = {
"client_id": client_id,
"redirect_uri": "http://localhost",
"client_secret": client_secret,
"grant_type": "authorization_code",
"code": code
}
response = requests.post(url="https://login.microsoftonline.com/common/oauth2/v2.0/token", data=data)
response_dict = json.loads(response.json())
with open("accesst.txt", mode="w") as accesst, open("refrt.txt", mode="w") as refrt:
refrt.write(response_dict['refresh_token'])
accesst.write(response_dict['access_token'])
好耶,刚刚完成了登录。接下来,由于临时传递的文件大多数都是大文件,因此不能用常规的上传功能进行上传,要用微软提供的“创建上传会话”的接口。具体代码网络上有很多,不过是依照文档调用多次API,此处不再赘述。
但是其实现在依然有问题。前些天,我用Windows的命令提示符(cmd),用open指令试图帮老师打开文件(真不是我当时在炫技或者闲的没事干,是因为文件资源管理器UI当时确实卡住了),结果发现open指令不能处理Windows共享远程文件夹和文件。这可把我吓了一跳——这说明Windows的共享文件夹和本地文件夹还是有一定区别的。万一Python的open()
函数也不能正常处理共享文件夹,怎么办?
Windows还是很不错的,它提供了一种一劳永逸的解决方案。我们可以把远程共享文件夹映射成一个虚拟的本地的磁盘,这样,文件操作就都跟访问本地文件完全一样了。具体做法就是,在文件资源管理器首页(就是会显示C盘、D盘等各磁盘的那个位置)上方菜单栏里面,点击“映射网络驱动器”,接着输入临时传递的路径\\172.17.17.17\临时传递\
,然后选择盘符(我这里选择的是在W盘上映射临时传递),稍等片刻即可大功告成。于是现在在W盘上我们也可以访问临时传递,现在Python也能更方便稳定地操作文件了。
接着是一些依照我的使用场景特别设计的逻辑。比如说,当调用API失败后,它会自动POST一个异常处理的URL,而该URL获取到POST指令后会将时间等数据存入数据库。
每周一~周五,9:30~9:55(课间操)、12:30~13:18(午休)、15:20-15:40(下午大课间)分别会自动启动,首先通过refresh_token去兑换最新的access_token,并通过access_token来同步OneDrive的文件。当发现有新文件时,直接上传;有相同标题的文件但是修改日期不同时,重命名为新的,保留两者。
三. 思考
历经三年,这个小小的想法终于付诸实践了。
但当我真正打开有着来自临时传递的文件的那个网站时,内心感受却并不与初一时所想的相同。
我们的学习,没有必要去纠结老师上课讲的一个细枝末节的拓展。我不是说它不重要,但是若如此,那么所有人都可以下课再打开老师的课件,记下课上未能记下的内容,那久而久之还有几个人听课呢?
真正重要的,不是课件。学习从来不只是知识的比拼。它还包括心理素质、身体健康素质,以及更重要的,一颗心。
我们的OneDrive可以带给我们约27000份课件,但它不能给我们带来——心理素质、身体健康、心。
如果我们心理不好,或是身体孱弱,势必会吃亏;如果我们一开始雄心满满,在三年中却逐渐丧失这一热情,那,也势必不能成功。那样,再多的课件也没有用。
向拥有这种心、并能一直葆有这颗心的所有人,致敬。
文章评论