在 iPhone 与 Windows 之间传输照片,看起来只是一个简单需求,实际经常卡在平台边界上:数据线需要驱动或授权,聊天软件可能压缩图片,网盘依赖上传带宽,系统自带的近距离传输协议又未必跨平台。
如果设备位于同一个局域网,更直接的做法是部署一个浏览器文件传输站。手机和电脑访问同一页面,选择对方设备后发送文件,不要求每台设备安装专用客户端。PairDrop 就属于这一类工具,其核心价值不是充当长期文件仓库,而是帮助浏览器发现彼此,并在网络条件允许时建立 WebRTC 数据通道。
这种方案适合家庭、实验室或小型办公网络中的临时传输,但需要先明确三个边界:
- 浏览器页面不是永久存储服务,接收方必须在线并确认下载。
- “点对点”不等于完全不需要服务器,设备发现和连接协商仍需要信令服务。
- WebRTC 能否直连取决于浏览器、局域网隔离、NAT 和防火墙;复杂网络中可能需要 TURN 中继。
下面以一台长期在线的 Linux 主机为例完成部署。Windows 也可以通过 Docker Desktop 运行相同容器。
工作原理:服务器负责牵线,数据通道负责传输
两个浏览器要建立连接,首先需要交换会话描述、候选网络地址等协商信息。浏览器无法凭空知道对方在哪里,因此会先连接 PairDrop 的服务端。服务端根据房间、网络或配对状态展示可用设备,并转发连接协商消息。
协商完成后,文件通常通过 WebRTC DataChannel 传输。DataChannel 基于加密的 WebRTC 连接,可以承载二进制分片。发送方读取文件,将其拆成浏览器能够处理的数据块;接收方依次重组,最后生成可下载对象。
STUN 和 TURN 在这里承担不同职责:
- STUN 帮助客户端了解经过 NAT 映射后的候选地址,本身不转发文件。
- TURN 在双方无法直接建立连接时中继流量,会消耗服务器带宽。
- 同一普通局域网内往往有机会直接连接,但访客网络、企业无线网络、运营商网络和严格防火墙可能阻止直连。
因此,服务端日志显示双方都已上线,并不能证明文件通道一定可以建立;判断部署是否成功,必须实际传输文件并检查完整性。
第一步:准备主机与目录
主机需要安装 Docker Engine 和 Docker Compose 插件。先确认环境:
docker version
docker compose version
创建独立目录:
sudo mkdir -p /opt/pairdrop
sudo chown "$USER":"$USER" /opt/pairdrop
cd /opt/pairdrop
生产环境不宜长期依赖浮动的 latest 标签。可以先拉取镜像,再取得当前镜像摘要:
docker pull lscr.io/linuxserver/pairdrop:latest
docker image inspect \
--format '{
{index .RepoDigests 0}}' \
lscr.io/linuxserver/pairdrop:latest
命令会输出带有 @sha256: 的完整镜像引用。将它写入 .env:
PAIRDROP_IMAGE=lscr.io/linuxserver/pairdrop@sha256:替换为实际查询到的摘要
镜像摘要必须来自本机拉取结果,不应照抄文章中的固定值。这样可以避免某次重新部署时,在没有评估的情况下自动换到不同镜像内容。
第二步:用 Docker Compose 启动服务
创建 compose.yaml:
services:
pairdrop:
image: ${
PAIRDROP_IMAGE}
container_name: pairdrop
restart: unless-stopped
ports:
- "3000:3000"
先检查 Compose 展开结果,再启动容器:
docker compose config
docker compose up -d
docker compose ps
docker compose logs --tail=100 pairdrop
查询主机局域网地址:
hostname -I
假设地址是 192.168.1.20,Windows 和 iPhone 连接同一个局域网后,分别访问:
http://192.168.1.20:3000
如果 Linux 主机启用了 UFW,需要按实际来源网段放行端口。不要直接对所有公网来源开放:
sudo ufw allow from 192.168.1.0/24 to any port 3000 proto tcp
sudo ufw status
网段应根据路由器配置调整。例如主机地址是 192.168.50.20,常见对应网段可能是 192.168.50.0/24,但最终应以主机的网络掩码为准。
第三步:执行可验证的传输测试
不要只验证网页能够打开。建议准备一个包含中文名、空格和较大二进制内容的测试文件,再计算哈希:
Windows PowerShell:
Get-FileHash .\测试照片.zip -Algorithm SHA256
Linux:
sha256sum '测试照片.zip'
在发送端页面选择接收设备,完成传输后,在接收端再次计算 SHA-256。两个值一致,才能说明文件内容未在传输或保存过程中发生变化。
测试至少覆盖以下场景:
- iPhone 向 Windows 发送一张照片。
- Windows 向 iPhone 发送一个非图片文件。
- 锁屏、切换应用或浏览器进入后台后再尝试传输。
- 发送文件期间刷新页面,确认失败时是否会明确中止,而不是误判为完成。
移动浏览器的后台执行会受到系统限制,大文件传输时应保持页面在前台,并暂时避免锁屏。具体限制可能随浏览器和操作系统版本变化,不能把一次成功视为所有设备上的固定行为。
第四步:需要公网域名时启用 HTTPS
仅在受控局域网内使用时,可以先通过 IP 和端口完成验证。如果需要跨网络访问,或者希望获得更完整的浏览器安全上下文,应使用域名和 HTTPS,并避免把容器端口直接暴露到公网。
将 Compose 端口绑定到回环地址:
services:
pairdrop:
image: ${
PAIRDROP_IMAGE}
container_name: pairdrop
restart: unless-stopped
ports:
- "127.0.0.1:3000:3000"
重新加载:
docker compose up -d
假设域名为 drop.example.com,DNS 已指向服务器,并且 Caddy 能监听 80 和 443 端口,可使用以下配置:
drop.example.com {
encode zstd gzip
reverse_proxy 127.0.0.1:3000
header {
Strict-Transport-Security "max-age=31536000"
X-Content-Type-Options "nosniff"
Referrer-Policy "same-origin"
}
}
加载配置前先格式化并校验:
sudo caddy fmt --overwrite /etc/caddy/Caddyfile
sudo caddy validate --config /etc/caddy/Caddyfile
sudo systemctl reload caddy
HTTPS 只保护浏览器到站点入口的连接,并不自动解决所有 WebRTC 连通性问题。若两个设备位于不同网络,页面可以正常显示,但传输一直停在连接阶段,就需要检查 ICE 协商和 TURN,而不是反复修改反向代理。
安全与运维建议
PairDrop 适合临时交换文件,不应被当作匿名公网文件入口。公网部署至少应考虑访问范围、日志保留、镜像升级和资源监控。
首先,优先通过 VPN 或反向代理身份认证限制访问者。若直接公开站点,陌生客户端可能连接信令服务,增加资源占用和骚扰风险。是否能够在代理层加入认证,取决于客户端页面、WebSocket 连接以及同源行为,配置后必须实际验证设备发现和传输流程。
其次,升级镜像时先拉取新版本并记录新摘要,在测试环境验证后再更新 .env:
docker pull lscr.io/linuxserver/pairdrop:latest
docker image inspect \
--format '{
{index .RepoDigests 0}}' \
lscr.io/linuxserver/pairdrop:latest
更新摘要后执行:
docker compose config
docker compose up -d
docker compose logs --tail=100 pairdrop
最后,关注浏览器内存。浏览器接收大文件时,具体实现可能需要在内存中缓存分片或构建 Blob。文件越大,越容易受到移动设备可用内存、浏览器后台策略和下载机制限制。对于数十 GB 的长期传输,SFTP、SMB 或专门的同步工具通常更可控。
常见问题
两台设备打开了页面,却看不到彼此
先确认两台设备访问的是同一个 PairDrop 实例。随后检查无线网络是否启用了客户端隔离;许多访客 Wi-Fi 会阻止终端之间直接通信。还应查看浏览器控制台、容器日志和反向代理日志,确认 WebSocket 或其他长连接没有被阻断。
能看到设备,但发送后一直等待
这通常说明设备发现成功,而 WebRTC 通道没有建立。可以让两台设备先连接同一个普通局域网进行对照测试。若同网成功、跨网失败,应重点检查 NAT、防火墙、ICE 候选和 TURN 配置。TURN 的端口、认证和传输协议必须与实际部署一致,不能仅凭网页可访问判断其可用。
iPhone 选择照片后文件名或格式发生变化
照片选择器可能提供经过系统处理的资源,结果受原始格式、系统设置和选择入口影响。如果需要保留特定原始文件,先在“文件”应用中确认实际文件,再从文件选择入口发送,并在接收端核对扩展名、大小和哈希。
页面传输时可以关闭吗
不建议。浏览器页面承担文件读取、分片发送或接收重组工作。刷新、关闭页面、锁屏以及系统回收后台标签页都可能中断任务。PairDrop 不是带有断点续传保证的后台同步系统,重要文件应在接收完成后校验。
是否需要把 3000 端口映射到公网
不需要。使用 Caddy 等反向代理时,应将容器端口绑定到 127.0.0.1,公网只开放 80 和 443。局域网直连模式则只对可信网段放行 3000 端口。
总结
自建 PairDrop 的部署本身并不复杂,真正需要理解的是浏览器文件传输的链路:服务端完成设备发现与信令交换,WebRTC DataChannel 承担数据传输,STUN 帮助寻找直连路径,TURN 则在直连失败时提供中继。
一个可维护的落地方案应包括固定镜像摘要、限制端口暴露、按需启用 HTTPS、用真实文件和哈希验证结果,并针对移动浏览器后台限制设计使用方式。若需求进一步扩展到超大文件、无人值守接收、历史版本或断点续传,就应转向更适合持久化传输的协议和工具,而不是继续把临时浏览器传输站改造成文件服务器。