Skip to content

dsh-photo2dsh-by-lan

Verified

dsh-photo2dsh-by-lan · v0.1.3 · MIT · Web UI

DSH 手机照片收件箱:手机(iOS 快捷指令 / Android HTTP Shortcuts)一键把照片经**局域网**传到这台 PC 的可配置目录并弹通知,Agent 直接可读;不依赖云、不用装手机 App、不改 app.asar。配置界面在侧栏 Plugins 本包页面。 | DSH LAN photo inbox: one tap on your phone (iOS Shortcuts / Android HTTP Shortcuts) sends photos over the LAN in

Install

dsh plugin add dsh-photo2dsh-by-lan

Confirm the layer applied with dsh --profile default --dump-config — see the install guide.

Source

Published to npm without a public repository. Inspect the package contents before installing.

Tags

Readme

dsh-photo2dsh-by-lan

English | 中文

手机拍照 → 局域网 → 这台 PC 的文件夹 → Agent 直接可读。

手机端用系统自带能力(iPad/iPhone「快捷指令」、Android「HTTP Shortcuts」)一键把照片 POST 到 PC; 插件负责接收、落盘到你自己指定的目录、弹 Windows 通知。不需要云、不需要飞书/微信、不需要装手机 App。

配置界面就在 DSH 侧栏 插件(Plugins)→ 本包页面里。


亮点

  • 手机一键 —— iOS 快捷指令或 Android HTTP Shortcuts 直接 POST 照片,手机侧不用装任何 App。
  • 落地目录你说了算 —— 在插件面板里改;照片立刻出现在那个目录。
  • Agent 直接可读 —— 落地目录就是普通目录,DSH 的 Agent 可以立刻读取/分析。
  • 到达即通知 —— 每张照片弹一条 Windows 通知(可点击跳回)。
  • 不依赖云、不用 IM、不装手机 App、不改 app.asar。
  • 可选 token 闸门 —— 默认关闭;关闭时以"局域网"为信任边界(见第 6 节)。
  • 离线验证台 —— 50 条断言,含我们真机上踩过的每一种故障。

1. 安装

方式 A:按包名(推荐,可迁移)

dsh plugin --profile desktop add dsh-photo2dsh-by-lan

dsh plugin 会自动把本 bundle 记进 profile 的 dsh.profile.bundles,不需要手工编辑 profile 文件。

方式 B:从本地源码装

dsh plugin --profile desktop add file:D:\path\to\dsh-photo2dsh-by-lan

⚠️ 两种方式只选一种。若 profile 里已经有指向本插件绝对路径的 insert 行,再按包名装一次会出现两行同名 entry。

装完之后:给端口放行一次防火墙

这是唯一需要管理员权限的步骤。默认端口 8787:

netsh advfirewall firewall add rule name="DSH Photo2DSH" dir=in action=allow protocol=TCP localport=8787 profile=any

撤销:

netsh advfirewall firewall delete rule name="DSH Photo2DSH"

⚠️ profile=any 不能省。省略 Profile= 字段才是"对所有网络类别生效"; 若只写 profile=public 而你的网络被 Windows 判成"专用",入站包会被静默丢弃, 手机端表现为"服务器已停止响应"(超时),极难定位。这是本项目实测踩过的坑。

顺带一提:别指望借用系统已有的放行端口。实测全机唯一"不限程序 + 不限类别"的 TCP 端口 被 http.sys 独占(EACCES),抢不过来。

装完重启 DSH(客户端半边需要重新装配)。


2. 在插件面板里配置

侧栏 插件(Plugins) → 本包页面 → 配置卡片:

配置项 说明
落地目录 照片落在哪里。会在此目录下自动建 <日期>\ 子目录
端口 默认 8787。改完要给新端口重新放行防火墙
单文件上限(MB) 默认 64
收到照片后弹 Windows 通知 默认开
每张照片写一张元数据卡(.json) 默认开。嫌目录里文件翻倍就关掉——见 §4
只接受局域网来源 默认开。非私网地址的上传一律 403;关掉 token 时它是唯一来源闸门
要求 token 默认开,强烈建议保持开启(理由见下)
手机要连的 IP 默认「自动」:枚举本机网卡、跳过 Hyper-V/VMware/WSL 等虚拟网卡、优先 192.168 网段。选错可手动指定
手机端要填的地址 只读,点「复制」直接拷走
token 只读

为什么落地目录建议填"当前会话工作区内的目录":DSH 的文件沙箱在 workspace-write 下只允许写工作区。 落在工作区外,宿主仍能写(插件用的是原生 fs),但 Agent 用工具读它可能受限。


3. 手机端配置

iPad / iPhone(快捷指令)—— 四步,第 3 步最容易漏

  1. 新建快捷指令,加 「选择照片」(或「拍摄照片」)。
  2. 加 「转换图像」 → 「转换为」选 JPEG、质量 80%。
    • 不加这步也能用,但实测同一张 8MP 图会从 ~1.5 MB 变成 ~12 MB(快捷指令按无损 PNG 递送)。
  3. 加 「获取 URL 内容」:
    • URL 填配置卡片里复制的那串(形如 http://192.168.x.x:8787/u/<token>);
    • 点「显示更多」→ 方法 POST → 请求体 文件;
    • ⚠️ 把「文件」栏的值改指「转换后的图像」 —— 漏这步就继续发原图,转换动作等于白加;
    • 最后加一个 「快速查看」 看返回。
  4. 运行。返回是中文短句,一眼能看懂:
✅ 照片已收到

格式:JPEG(1.63 MB)
文件:20261003232612-4d2b993e-image.jpg
时间:2026-10-03 23:26:12

已存入 PC 的收件箱文件夹

首次访问局域网时 iPadOS 会弹「允许访问本地网络设备」——必须允许。 这与防火墙无关,是 iOS 独立的一道闸;拒绝后请求会被系统直接掐断。

想让相册里"只传我想传的那几张":快捷指令 ⓘ → 打开「在分享表单中显示」→ 接收类型勾「图像」, 之后在照片 App 里选图 → 分享 → 本快捷指令。

Android(HTTP Shortcuts)

同一个 URL、同样的 POST + 文件体即可。协议两端完全一致,不另设。


4. 数据怎么落

<你配置的目录>\
  └─ 2026-10-03\
       ├─ 20261003232612-4d2b993e-image.jpg     ← 本地时间戳-内容哈希前8位-清洗后的原名
       └─ 20261003232612-4d2b993e-image.json    ← sidecar:原名/备注/时间/字节数/sha256/格式/来源
  • 文件名带内容哈希前 8 位,所以肉眼就能核对两张图是否同一张。
  • 同秒同名不会互相覆盖:落盘用 link(),目标已存在就自动追加 -1、-2。 (rename 会静默覆盖——实测同一秒发多张 image.jpg 会只剩一张且毫无提示,这条是专门修的。)
  • 只认魔术字节,不信任 Content-Type:实测快捷指令送来时头写 image/jpeg、字节却是 PNG。
  • 那张 .json 是可关的。它记的是文件名给不出来的东西:哪台设备发的、原始文件名、备注、sha256、嗅探所得格式。 嫌目录里文件翻倍,就在面板里取消勾选「每张照片写一张元数据卡」——之后目录里只剩照片本身。 这些元数据即使在开关打开时也不会影响照片落盘成功(写元数据失败只记日志)。

5. 故障排查

现象 原因 处理
手机显示"服务器已停止响应"(超时) 入站包被防火墙静默丢弃 按第 1 节加防火墙规则;必须带 profile=any
返回 ❌ 上传失败:地址里缺少正确的 token URL 少了 /u/<token> 这一段 从配置卡片重新复制完整地址
返回 ❌ 这不是一张能识别的图片 发出去的确实不是图片字节 检查快捷指令里「文件」栏是否真的选上了图像变量
返回里写着"仍是 PNG" 「转换图像」没接到「文件」栏 见第 3 节第 3 步
面板里看不到本包页面 那一行 Loader 被关闭了,或 profile 未重启 官方契约:客户端半边挂在"说明符恰为包名"的那一行上,关掉那一行卡片就一起消失
端口启动失败 端口被别的程序占用 面板里换一个端口,并给新端口放行防火墙

日志:$DSH_HOME\photo2dsh-by-lan.log 配置:$DSH_HOME\photo2dsh-by-lan.json


6. 安全边界

默认配置(requireToken: true + lanOnly: true):

  • 只监听局域网(0.0.0.0:<port>),不放公网。
  • token 走 URL 路径,128 bit 随机、首次启动自动生成并持久化(这就是"可迁移"——换台机器不用手配)。
  • 401 不会回显正确的 token:那等于把它送给任何打错地址的人。
  • 设置页(GET /)只对 127.0.0.1 开放:它含 token,不能让局域网随便看。
  • /health 不含 token(uploadUrl 里也含 token,所以被一并剥掉)。

不想用 token?可以,但两道闸门要一起上

在面板里把 「要求 token」关掉 即可(地址就变回 http://<IP>:<port>/,快捷指令里更好填)。但另外两道得在:

闸门 挡什么 默认
只接受局域网来源(lanOnly) 非私网地址(公网)的上传一律 403 ✅ 开
拒跨源 Origin 网页跳板:你浏览的恶意网页借你的浏览器 POST 过来 ✅ 永远生效,无开关

⚠️ 第二道不能省,也不能用第一道代替。 网页跳板走的是你自己的浏览器,服务端看到的来源是 127.0.0.1 —— 在"局域网/私网"的定义里完全合法。只有 Origin 头能识别它:浏览器跨源请求一定带 Origin,而 iOS 快捷指令这类原生客户端根本不带。

关掉 token 后仍然成立的保护(与 token 无关):路径清洗(防目录穿越)、只收图片(415)、体积上限(413)、落盘用 link() 防覆盖。 也就是说最坏情况是"局域网里的设备往你的收件箱灌垃圾图片",不可能写到别的目录、不可能覆盖已有文件、不可能放可执行内容。

仍然建议:只在自有可信网络(家里/办公室)里关。咖啡馆、酒店、合租的 Wi-Fi 别关。


7. 已知限制

  • 仅 Windows(通知走 Windows PowerShell 5.1 的 WinRT;package.json 已声明 os: ["win32"])。
  • 通知横幅上的应用名显示为 Windows PowerShell。要显示成自己的名字需要往 HKCU\Software\Classes\AppUserModelId 注册 AUMID。
  • 不做:云中转 / 飞书微信通道 / 界面里的照片浏览器 / 自动把消息注入会话 / 自动清理。
  • 卸载时不会删除你已收到的照片(那是你的数据)。

8. 开发

# 离线验证台(50 条,含全部真机故障复现用例)
node test/photo2dsh.test.mjs

架构:

文件 职责
core.mjs 纯逻辑(命名/白名单/魔术字节/配置文档解析),无 I/O,可离线测
receiver.mjs 宽容的原始 TCP 接收(不用 node:http,理由见文件头注释)
store.mjs link() 原子落盘 + sidecar
notify.mjs + scripts/toast.ps1 Windows Toast(PS 5.1;脚本纯 ASCII 以绕开 BOM 坑)
index.mjs 宿主接线:配置分层、receiver 生命周期、/api/photo2dsh-by-lan.config 路由
client.js 客户端半边:注册进官方槽位 plugins.bundle.config 的配置卡片