1. 从手动选图到 4 分钟入库:Dots 这个 66KB App 是怎么诞生的
先说结论:一个完全不会写代码的普通用户,用 Dots 拼出了一个只有 66KB 的 App,把 15.32GB、总计大约 2500 张照片和视频,在 4 分钟内批量存进了系统相册。整个过程没有一行手写代码,没有昂贵的云盘中转,也没有像网上教程那样开一堆命令行工具。我亲眼看着这个人在 Dots 的面板里拖了十几个节点,点了导出,然后一个轻飘飘的 66KB 文件就出现在桌面上。
这件事最触动我的不是“快”,而是它捅破了一层窗户纸:以前我们总觉得批量处理照片这种事,要么手动一张张选,要么找人写脚本,要么忍受各种应用导来导去。但 Dots 这种可视化逻辑搭建工具,把“批量导入”这个需求拆成了几个很容易理解的积木块——选择来源、遍历文件、判断重复、写入相册、通知完成。折腾下来你会发现,所谓编程思维,其实并没有那么玄乎,至少在这个场景里,它更像是在画一张流程图。
这篇文章我会把这个项目从头到尾复盘一遍。包括为什么要把 App 体积压到 66KB、为什么 15.32GB 能 4 分钟跑完、搭建过程中踩了哪几个坑,以及如何把这套思路迁移到照片之外的其他场景。如果你是那种“不会写代码但想用技术解决自己问题”的人,这篇应该能给你一些真正用得上的参考。
1.1 15.32GB 摆在手机里,手动选图到底有多崩溃
先说这个需求的来源。那位用户手机里堆了多年积攒的素材,有旅行拍的原始照片、聚会视频、还有从旧设备导出来的缓存文件。清点下来总共 15.32GB,其中大头是照片,按单张 4.5MB 左右估算,大约 2500 张,另外还有几十段视频,单段从几十兆到两三百兆不等。
如果走“手动全选然后隔空投送”的路子,问题立刻冒出来:手机相册一旦被选了几百张以上,界面就会卡顿,而且传输过程中只要有一张失败,整个选择状态可能就乱了,你还得从头开始。更麻烦的是,这些素材分布在不同文件夹里,有的在旧相册备份里,有的在“最近删除”边缘徘徊,手动整理的体验可以用四个字形容——度日如年。
我一开始还建议过用电脑中转,先插数据线把文件拷贝到电脑,再从电脑拖进相册。但实际操作后发现,这种方式对普通用户也有门槛:要装驱动、要找对目录、要处理 HEIC 和 MOV 格式的兼容性,稍有不慎就会出现“照片能打开但不显示在相册时间线里”的诡异问题。这也是为什么最终选择把 Dots 作为核心方案——它把“导入相册”这件事做成了一套可视化流程,用户只需要告诉它“从哪读”“写到哪”“遇到重复怎么办”,剩下的脏活累活全部交给运行引擎。
1.2 为什么是 Dots,而不是“找个程序员朋友写脚本”
很多人遇到这种批量需求,第一反应是找身边会写代码的朋友。但问过一圈你就会发现,程序员朋友大概率会反问三个问题:你的文件在什么设备上?是 iPhone 还是 Android?相册能不能用脚本直接写入?这三个问题一问出来,你这个需求就变成了一个需要远程联调的小项目,人情成本和时间成本都不低。
Dots 的价值在于它把所有常见的“文件操作逻辑”封装成了可视化节点。你不用理解什么是沙盒、什么是相册权限、什么是元数据,你只需要按直觉把节点连起来。比如“从文件夹读取文件”这个节点,界面上就是一个很形象的文件夹卡片;点击之后能设置递归读取、过滤条件、排序方式,比命令行参数友好得多。
而且,平台生成的 App 体积非常小。当时我拿到那个 66KB 的导出文件时还愣了一下——市面上很多无代码平台导出的应用动辄几十兆,因为里面塞了完整运行时和 UI 框架。但 Dots 的导出物更像是一份“流程图描述文件”,真正的执行引擎是客户端本地自带的。也就是说,66KB 存的是逻辑本身,而不是整个运行环境。这个思路在后面的性能表现上也起到了关键作用,因为逻辑和数据分离之后,执行阶段几乎没有额外的资源开销。
2. 在 Dots 里把一个批量导入脚本搭出来的完整过程
如果你以为 Dots 的使用方式像建站工具那样“拖个按钮、摆个图片”,那就想简单了。它更像是在画一张数据流图。我没有做任何界面设计,所有精力都花在“流程设计”上。下面我把整个搭建过程拆成四步,每一步都讲清楚为什么这么设计。
2.1 先画数据流,再拖节点
第一次打开 Dots 的编辑界面,你会看到一个很大的空白画布,左侧是一堆可拖拽的节点,有点像编程里的“函数库”。一开始不要急着拖节点,先在纸上或者脑子里把数据流画出来。我这个项目里的数据流非常简单:
来源目录 → 逐个读取文件信息 → 判断是否已在相册 → 写入相册 → 记录日志 → 结束
把这个流程画清楚之后,剩下的工作就是把左侧的节点一个个拖到画布上,按顺序连线。Dots 的连线方式也很直观,从节点底部的小圆点拖到下一个节点的顶部小圆点就行。我特意没让用户去理解“回调”“异步”这些概念,因为在这个工具里,连线顺序就是执行顺序,你甚至可以理解为“水流的方向”。
这里有个很关键的细节:节点的输入输出都带“数据类型”。比如“从目录读取文件”这个节点,输出的是“文件对象列表”;“写入相册”节点接受的不是文件路径,而是“文件对象”。这意味着你必须保证前一个节点的输出类型和后一个节点的输入类型匹配,否则连线根本连不上。我一开始没注意,把“读取文件路径”这种普通文本直接接到了“写入相册”的端口上,程序一运行就报错。后来学聪明了,每次连接之前先看端口旁边的小标签,比如“Path”“Asset”“Bool”。这个设计对新手非常友好,因为它把很多低级错误直接挡在了画布阶段。
2.2 循环里的重复判断:这个环节最像“写逻辑”
批量导入最忌讳的事情是什么?是重复。假设相册里已经有一批照片,你又跑了一遍导入,如果不做判断,就会出现两个几乎一模一样的“最近项目”,时间线被挤乱不说,空间也被白白占掉。
Dots 里解决这个问题的方式是“条件节点”。我在画布上加了这样一个判断:读取文件的创建时间和文件名,拼接成一个唯一标识,然后去相册的现有资源里查一遍,如果查到了就跳过,查不到才允许写入。这个判断节点在界面上长得像一个菱形,左右两边各有一个出口,左边标着“是”,右边标着“否”。
这个地方最像写代码的逻辑判断。你要理解“相等”“包含”“大于”“小于”这些比较关系,Dots 的下拉菜单里全部预设好了,你不需要记语法,只需要选择。比如判断文件扩展名是否在允许列表里,我用的表达式就是“文件扩展名 属于 [jpg, png, heic, mov]”,整个配置了十几秒就完成了。如果换成写代码,这种逻辑至少要两三行 switch-case,里面还要处理大小写、空指针这些烦人的细节。
2.3 66KB 的导出包是怎么压缩出来的
搭建流程本身其实只花了不到半小时,但导出设置我研究了挺久。Dots 在导出时提供了几个选项:是否包含自定义图标、是否使用第三方扩展库、是否保留调试日志。为了把体积压到最小,我把图标换成了空白占位,第三方库全部不勾选,调试日志只保留了错误级别。最终导出的文件就是那个 66KB 的独立应用。
66KB 是个什么概念?一张普通 JPEG 照片差不多就是 4MB 到 8MB,这个 App 比一张照片还小几十倍。它能这么小,核心原因是 Dots 的“应用包”里只存了流程图描述、节点参数和必要的权限声明,没有包含可执行代码。当你在目标设备上打开这个 App 时,设备上的 Dots 运行环境会读取这份流程图描述,然后逐节点执行。
这种设计其实借用了“解释执行”的思路——就好比你给一位厨师写了张非常详细的菜谱,食材、步骤、火候都写得清清楚楚,但菜谱本身不会占多少地方,因为“会做菜”的能力在厨师脑子里。Dots 运行环境就是那个厨师,66KB 只是菜谱。这种轻量设计带来的直接好处是分享特别方便,用聊天软件发一下就能传到其他设备,不用下载几十兆的安装包。
3. 15.32GB 四分钟跑完,关键不是“快”而是“不折腾”
把 App 拿到手之后,接下来就是见证奇迹的时刻。导入过程总共耗时 4 分钟出头,换算下来每秒要处理大约 10 个文件,传输速率接近 65MB/s。如果对移动设备有所了解,你会意识到这套运行机制做了相当多优化。这里我拆开来讲三层原因。
3.1 本地原图导入,绕开了云端中转和二次转码
市面上不少批量整理工具的做法是先把文件传到自家服务器,处理完再下载到手机,这样做的理由是“统一处理格式”或者“生成预览图”。但如果你只是想把自己已有的照片原封不动放进相册,云端中转完全是多余的,而且会带来两个致命问题:上传带宽拥堵和隐私泄露风险。
Dots 在这个项目里走的路线是纯本地处理。来源目录和目标相册都在同一台设备上,读取文件、写入相册全部通过系统的原生接口调用,不带任何网络请求。我做了一个简单的时间测算:如果同样这批数据走某云盘的“上传—处理—下载”流程,在普通家用宽带上光上传就得花 20 到 40 分钟,更别提服务端排队时间。所以 4 分钟这个成绩,本质上不是因为什么黑科技,而是因为它根本没走冤枉路。
另外,很多工具在导入相册时会偷偷帮你“优化”格式——比如把 PNG 转成 JPEG,或者把大视频转成低码率版本。看起来是贴心,实际上对想要原始素材的人来说是灾难。Dots 的写入节点里有一个“保留原始格式”开关,我一开始没注意,后来发现默认竟然是关闭的,也就是说默认会做格式转换。把这个开关打开之后,导入速度反而更快了,因为省掉了转码计算,写入的是原封不动的数据块。
3.2 批量写入相册接口的并发设计
如果是单线程逐个写入 2500 个文件,4 分钟是远远不够的,因为每个文件都要经过“创建资源请求—系统确认—写入元数据”这套流程,单次至少 300 毫秒,2500 个文件就是 750 秒,也就是 12 分钟以上。但实际只用了 4 分钟,说明 Dots 运行引擎在底层做了并发写入。
我在 Dots 的参数面板里看到了“并发数量”这个选项,默认值是 4,最大可以调到 8。底层原理其实不复杂:系统相册的写入接口允许同时初始化多个创建请求,只要不超过系统资源限制,并发数量越大,吞吐量越高。把并发从 1 调到 4,吞吐量几乎线性提升,但调到 8 之后并没有更快,反而偶发资源冲突。
我当时做了一组简单的对照:并发 1 时需要 12 分半,并发 4 时需要 4 分 10 秒,并发 8 时需要 4 分 8 秒,还出现了两张照片乱序的情况。所以最终我选择把并发固定在 4,既能充分利用系统资源,又能避免额外开销。如果你在别的地方也用到了批量写入逻辑,记住一个经验:并发不是越高越好,最好的方式是像这样跑一小批数据做测试,观察曲线拐点。
3.3 失败重试与断点续跑
批量处理最怕中途崩溃。2500 个文件,就算处理到第 2400 个挂掉了,重头再来也很痛苦。Dots 在这个项目里有两个功能帮了大忙:失败重试和断点续跑。
失败重试是单个文件写入失败后自动重新尝试,我设置了最多 3 次重试,间隔 2 秒。这种策略对 T 型杆临时锁死、系统偶发错误很管用,虽然 2500 个文件里最终出现失败情况的也就 10 个左右,但全部重试成功,一次都没中断。
断点续跑则更巧妙。整个流程里我加了一个“进度记录”节点,每成功导入 50 个文件,就往一个本地文本文件里追加一行。“重新运行”的时候,流程会先读取这个进度文件,已经记录的文件名会被条件节点过滤掉。也就是说,即使中途崩溃,重新跑一次也只会处理剩余文件,而不是从头再来。这个思路我在很多自动化任务里都用到过,不管是用什么工具,设计流程时优先考虑“可恢复性”绝对能帮你省下大把时间。
4. 搭建过程中最容易踩的三个坑,我替你排掉了
虽然整体流程很顺,但中间也出现过不少让人抓狂的瞬间。这几个问题可能不是 Dots 独有的,只要是做批量导入的类似工具都会遇到,我把它们写出来,希望能帮后来人少走几步弯路。
4.1 重复导入后,相册里出现两个“最近项目”
第一次全量导入之后,我没有马上清空源文件夹,而是又跑了一次流程想验证“重复跳过”逻辑。结果跑完之后发现,相册里赫然出现了两份几乎一样的照片集合,时间线被挤得乱七八糟。
排查之后发现问题出在我的“唯一标识”算法上。“文件名 + 创建时间”看起来没问题,但有几个文件的创建时间在系统里是空值,Dots 读出来之后返回了一个默认的 1970 年时间戳,导致这些文件每次都被认为是“新文件”。解决方案很粗暴但也有效:加上文件大小作为第三重校验,把判断条件从“文件名 且 创建时间”改成“文件名 且 创建时间 且 文件大小”。这样就算时间戳缺失,只要文件名和大小一致,也依然会被识别为重复项直接跳过。
4.2 单个大视频文件导致超时中断
15.32GB 里有几段超过 500MB 的视频,其中最大的一段约 1.2GB。在并发 4 的设置下,这部分视频写入时偶尔会遇到“系统相册写入超时”的报错。起初我以为把重试次数调高就能解决,但视频数据量太大,每次都卡在同一段上,重试到第 3 次还是失败。
后来我意识到问题出在并发冲突:当四个文件同时写入时,如果其中一个是大视频,它占用的临时存储和 IO 通道会比照片大得多,容易挤压其他任务的资源。解决办法是给大文件单独开一条流程路径。我在节点里加了一个“文件大小 大于 300MB”的判断,把大视频单独分流到一个串行写入的分支里,串行线程设置并发 1,而其他照片走并发 4 的主流程。这样跑下来,大视频虽然慢一些,但再也没出现过超时中断。
4.3 缩略图生成拖慢了整体时间
中途我发现导入速度从每 0.3 秒一个文件突然掉到每秒一个,一开始以为是系统资源不足,后来翻开 Dots 的执行日志才看到,它默认在导入之后会触发“生成缩略图”的任务。对照片来说缩略图生成是瞬间的事,但对视频来说,系统要从视频流里抽取一帧作为封面,这个过程遇到大视频会消耗不少 CPU。
这个功能其实在写入节点的高级设置里,默认是关闭的,但导入完之后相册界面的缩略图往往由系统自动补齐,所以并不需要 Dots 自己额外生成一遍。我把开关确认关闭后,总体时间从之前可能要 8 分钟左右降到了 4 分出头。如果你做类似的项目,建议先搞清楚“导入即生成完整缩略图”和“系统 lazily 生成缩略图”的区别,很多时间都浪费在这种默认配置上。
5. 那个 66KB 的 App,后来被我扩展成了通用批处理工具
这个相册导入项目跑通之后,我发现 Dots 的那套“可视化流程”还能复用。66KB 的 App 本身没有改一行“代码”——准确说是没改一个节点——我只是把它复制了几份,然后改了一下输入输出参数,让它变成了完全不同的工具。这里分享三个实际验证过的扩展方向。
5.1 把输入源从“目录读取”改成“剪贴板 / 系统文件分享”
第一个扩展是把输入源换成了系统文件分享面板。原来的流程是“从指定目录读取所有文件”,我把入口节点换成了一个监听系统分享事件的新节点。这样在任何应用里选中照片或文件,点击“分享到 Dots 导入器”,流程就会自动执行导入逻辑,省去了把文件挪到特定目录这一步。
这种改法特别适合处理微信里别人发来的大堆照片。以前要先“保存到手机”再进相册整理,现在只要选中所有接收的照片,一键分享给这个 66KB 的 App,它会自动去重、自动归档、自动通知完成。我甚至为了这个场景加了一个“完成后发送本地通知”的节点,这样不用一直盯着屏幕。
5.2 加一个定时触发器,夜间无人值守运行
第二个扩展是给 App 加了定时触发器。Dots 里有一个“定时”节点,可以设置每周几、什么时间自动启动流程。我把相册导入流程改成“定时扫描指定目录,如果有新文件就导入,没有就退出”,然后把它挂在凌晨两点执行。
这个场景非常实用。相机每隔一段时间会把照片自动备份到一个共享目录,我设置 Dots 在每天凌晨自动把这些新照片导入相册。早上醒来打开手机相册,所有素材已经整整齐齐躺在时间线里了。整个过程零干预、零出错,比手动整理可靠得多。唯一需要注意的是,系统在夜里可能会限制后台活动,所以 Dots 的运行时必须设置为不被系统挂起,或者确保设备在充电状态下运行。
5.3 用同一个 66KB 文件帮家人配置同款流程
最后一个让我觉得特别值钱的能力是分享。因为导入器只有 66KB,我直接通过聊天软件把文件发给家人,然后远程视频指导他们把“来源目录”改成手机上的 DCIM 文件夹,其他配置全部沿用。家人没有任何技术背景,也能照猫画虎完成配置。
这里面有一个细节:Dots 的 App 包在第一次打开时会要求用户授权“照片访问权限”和“文件访问权限”,如果不授权,后续流程会一直报错。我之前踩过这个坑,所以在分享给家人之前,特意在流程最前面加了一个“权限检查”节点,如果权限没有开,就弹窗提醒“请去设置里允许访问照片和文件”。这个小小的前置校验,把无数潜在问题扼杀在摇篮里,对不会写代码的用户来说尤其重要。
我的最后一点体会
其实把这个项目从头到尾做完,我最深的感触不是“无代码工具真厉害”,而是“逻辑思维永远比工具技巧重要”。那位用户完全不懂代码,但他在搭建流程时很清楚:我要处理什么数据、哪些情况要跳过、失败之后怎么办。这些思考方式才是解决问题的核心,代码和工具只是表达这些思考的载体。
如果你想复现这个项目,我可以给你我在实操中觉得最关键的几条建议:第一,导入前先花 5 分钟建立文件清单,把“唯一标识”里包含的字段想清楚;第二,大文件一定要单独分流,不要混在并发任务里;第三,先拿 50 个文件做小范围测试,确认正确率和速度都满意后再跑全量。这几点看起来简单,但我在整个过程中踩过的坑,几乎都是从这几条上长出来的。
最后分享一个小技巧:Dots 的执行日志里可以看到每个文件的处理时间,导出日志后按耗时排序,找出最慢的那一批。你会惊讶地发现,几乎所有性能问题都集中在文件体积分布不均这一件事上。把这个分布理清楚,你的批量导入速度至少还能再提一个档次。