FilePizza 浏览器文件传输:从"跑通"到"能上线"的分阶段通关路径
【免费下载链接】filepizza:pizza: Peer-to-peer file transfers in your browser项目地址: https://gitcode.com/GitHub_Trending/fi/filepizza
FilePizza 是一个基于 Next.js 15 和 WebRTC 构建的 P2P 文件传输服务:文件在上传方与下载方浏览器之间直连传输,不落服务器,并自带密码保护、多文件打 zip、流式下载等能力。本文按阶段拆解它从"跑起来"到"可上线"的几处隐患,帮你把一个能交付的 FilePizza 部署出来。
一、项目全景
功能模块一览:
- WebRTC 直连传输:基于 PeerJS,文件从浏览器到浏览器直传,服务端只做信令与通道元数据
- 短链/长链通道体系:每次传输生成 8 位短链和 4 词长链,TTL 默认 1 小时,存于 Redis 或内存
- 密码保护与举报:上传可加密码锁,
/reported页面处理违规链接举报 - 流式下载:Service Worker + StreamSaver 把 WebRTC 数据流直接落盘,不占浏览器内存
- 多文件 zip:一次传多个文件时,下载方收到流式打包的 zip
- 暗色模式与移动端:next-themes + View Transitions,Mobile Safari 可用
最小可运行命令:
git clone https://gitcode.com/GitHub_Trending/fi/filepizza cd filepizza pnpm install pnpm dev # 打开 http://localhost:3000环境要求:Node v18+,包管理器必须用 pnpm(仓库强制,npm/yarn 不被支持);要做全链路 WebRTC 测试(Redis + coturn)时运行pnpm dev:full会自动拉起对应容器。
先说结论:跑通≠能上线。下面按配置与凭据、数据与权限、构建与环境、体验与一致性四个阶段拆雷。
二、分阶段排雷
阶段 A 配置与凭据:环境变量是唯一入口
生产 compose 引用了.env,仓库却不带模板
你会看到什么:照文档换成生产部署后,docker compose -f docker-compose.production.yml up直接报env file .env not found。
定位:docker-compose.production.yml 的 filepizza 服务声明了env_file: - .env,但仓库里既没有.env也没有任何示例文件。全部可用变量清单在 README.md 的 Configuration 小节:REDIS_URL、COTURN_ENABLED、TURN_HOST、TURN_REALM、STUN_SERVER、PEERJS_HOST、PEERJS_PATH。
处理办法:在 compose 文件旁手动创建.env按清单填写。只部署单实例、暂不开 TURN 时,最小配置是REDIS_URL加COTURN_ENABLED=false。
验证:执行docker compose -f docker-compose.production.yml config,输出里能看到你设置的环境变量且无报错即通过。
信令默认走公有云0.peerjs.com,"不过中转服务器"只对文件本身成立
你会看到什么:README 强调"data is never stored in an intermediary server",但你不设置PEERJS_HOST时,PeerJS 的信令(双方发现彼此、交换连接参数的那一步,不含文件数据)实际走公有 PeerJS 云。网络环境对公有云不友好时,连接会一直握手不上。
定位:src/app/api/ice/route.ts 中peerjsHost = process.env.PEERJS_HOST || '0.peerjs.com',默认值就是公有云。
处理办法:自建 PeerJS 服务器并把PEERJS_HOST/PEERJS_PATH指向它,仓库提供了pnpm start:peerjs脚本(运行./bin/peerjs.js)。
验证:启动服务后执行curl -X POST http://localhost:3000/api/ice,确认返回的host是你自建地址而非0.peerjs.com。
阶段 B 数据与权限:存储选型决定链接"活不活得过重启"
进程一重启,链接全灭:默认是内存存储
你会看到什么:开发机上刚生成的链接还正常,重启服务后访问直接 404;多实例部署时链接"时灵时不灵"。
定位:src/channel.ts 的getOrCreateChannelRepo()只有两种实现——设置了process.env.REDIS_URL时用RedisChannelRepo,并打印[ChannelRepo] Using Redis storage;否则用MemoryChannelRepo,打印[ChannelRepo] Using in-memory storage。链接 slug 到上传者 PeerID 的映射和 TTL 全存在这里。
处理办法:生产部署必须设REDIS_URL。docker-compose.production.yml 已内置 redis 服务并配好REDIS_URL=redis://redis:6379,保留即可。
验证:启动服务后在日志里确认出现Using Redis storage;生成一个链接,重启容器,再用新浏览器窗口打开链接仍能解析。
开发环境 NAT 穿透失效:TURN 中继端口全被注释掉了
你会看到什么:两个用户跨网络传输(一个公司内网、一个手机热点)时双向干等,连不上;同一套配置放生产却正常。
定位:docker-compose.yml 中 coturn 服务的# Relay Ports一行和49152-65535/udp端口段全部被注释,启动命令也没有--min-port/--max-port;而 docker-compose.production.yml 开放了60000-60128/udp并带对应参数。TURN 是中继服务器,中继端口不开放,打洞失败后这条兜底路径就断了。
处理办法:跨网络实测请用pnpm dev:full(package.json 中它会拉起同一份 dev compose),并在你自己的部署副本里参照生产 compose 补上中继端口段。
验证:docker compose logs coturn观察跨网传输时是否出现中继端口分配记录,或用"手机热点 + 办公室网络"实测一次成功。
阶段 C 构建与环境:镜像、构建与启动链路
redis:latest和 coturn 镜像不锁版本,今天能构建明天可能翻车
你会看到什么:第一次构建部署一切正常;几周后重跑pnpm docker:build,行为不一致或拉到的新镜像直接不兼容。
定位:两份 compose 文件里 redis 写的是image: redis:latest,coturn 写的是image: coturn/coturn(隐式 latest),第三方镜像不锁版本,构建不可复现。
处理办法:在自己的部署副本里把验证过的版本固定下来(如redis:7-alpine加 coturn 的具体版本号),并记录进部署文档。
验证:执行docker compose config --images,确认所有镜像都解析为具体版本 tag。
构建后不懂 standalone 就启不起来
你会看到什么:pnpm build之后你习惯性地next start,可容器入口却是node server.js,两条启动路径并不等价,目录结构对不上。
定位:next.config.js 设置output: 'standalone',构建产物是一个自带server.js、不依赖完整 node_modules 的最小服务器;package.json 的 build 脚本额外执行cp -r public .next/standalone/与cp -r .next/static .next/standalone/.next/;Dockerfile 的 runner 阶段只拷贝这三部分,且以非 root 的USER node运行。
处理办法:生产启动路径不要走next start,要么直接node .next/standalone/server.js,要么以 Dockerfile 为唯一参照。
验证:执行pnpm docker:build && pnpm docker:up,然后curl http://localhost:8080确认返回 200。
阶段 D 体验与一致性:开发行为与生产行为对齐
开发环境看到重复的两条连接,生产只有一条
你会看到什么:pnpm dev时上传方和下载方建立 peer 连接会重复(控制台看到重复的 connect 事件、进度条抖动),切到生产构建就正常了。
定位:next.config.js 刻意设置reactStrictMode: false,注释写明原因——uploader 和 downloader 都用 useEffect 监听 PeerJS 事件,StrictMode 在开发模式的双重调用会把连接创建两次。
处理办法:保留该配置,不要为了"开发更严格"随手改回 true;确需开启时,先参考 src/hooks/useUploaderChannel.ts 的写法,把事件订阅收敛到每个连接只订一次。
验证:pnpm dev后开上传页与下载页各一个窗口,在控制台确认 PeerJS 连接只建立一次。
上传者一走,链接立即变孤儿
你会看到什么:传输完成后上传者关闭页面,别人再用同一链接发起新下载立刻失败,容易被当成 bug 上报。
定位:这是项目定位而非缺陷——文件本体只存在于上传者机器,README.md FAQ 明确写了关闭浏览器后 "URLs will no longer work"。另外通道 TTL 默认 1 小时(src/config.ts 中channel.ttl: 60 * 60),即使上传方还在,链接超时也会失效,可通过/api/renew接口续期。
处理办法:在产品文案里明确告知"请保持页面开启直到传输完成";需要更长有效期就调用 renew 时传入 TTL 参数。
验证:生成链接后关掉上传窗口,确认新窗口打开链接不可用;再保留上传页、调/api/renew续期,确认原到期时间点后链接仍可用。
三、上线自检表
| 检查项 | 怎么做 | 通过标准 |
|---|---|---|
.env配齐 | 按 README 变量清单填写REDIS_URL、COTURN_ENABLED等 | docker compose -f docker-compose.production.yml config无报错且值符合预期 |
| PeerJS 自建 | 部署 PeerJS 并设置PEERJS_HOST/PEERJS_PATH | POST /api/ice返回host为自建地址 |
| Redis 生效 | 查看服务启动日志 | 输出[ChannelRepo] Using Redis storage |
| 链接存活重启 | 生成链接 → 重启容器 → 打开链接 | 链接仍可解析 |
| NAT 穿透 | 跨网实测并查 coturn 日志 | 跨网传输成功,日志有中继端口分配 |
| 镜像版本锁定 | 副本中固定 redis/coturn tag | docker compose config --images全部为具体版本 |
| standalone 启动链路 | pnpm docker:build && pnpm docker:up | curl http://localhost:8080返回 200 |
| 全量 CI 自检 | pnpm ci(lint、format、type-check、单测、构建、e2e、docker 构建) | 全部通过 |
按这四个阶段过一遍,FilePizza 就从本地 demo 变成了"链接不过夜消失、跨网可穿透、构建可复现"的可交付状态。过程中你摸熟的环境变量单一配置入口、状态存储单一事实源、镜像锁版本、开发生产行为对齐这几套方法,在任何自托管部署场景都能直接复用。🚀
【免费下载链接】filepizza:pizza: Peer-to-peer file transfers in your browser项目地址: https://gitcode.com/GitHub_Trending/fi/filepizza
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考