FilePizza WebRTC 点对点文件传输:从本地跑通到生产交付的完整指南
2026/9/17 8:13:12 网站建设 项目流程

FilePizza WebRTC 点对点文件传输:从本地跑通到生产交付的完整指南

【免费下载链接】filepizza:pizza: Peer-to-peer file transfers in your browser项目地址: https://gitcode.com/GitHub_Trending/fi/filepizza

FilePizza 是基于 Next.js 15 + WebRTC 的浏览器点对点文件传输应用:文件不经过服务器中转,而是由上传方浏览器直连下载方浏览器完成。本文按部署流水线把它从"能跑起来"推进到"可交付",帮你独立完成一次可信的本地验证与生产前检查。

锁定 Node 18 与 pnpm 跑通本地开发服务

现象与风险:项目依赖 pnpm 的严格依赖解析与 workspace 能力,用 npm/yarn 安装容易得到与锁文件不一致的依赖树;而 Next.js 15 需要 Node 18 及以上,Node 版本过低会在启动阶段直接报出cryptofetch相关的 API 缺失错误。开发期还有一个隐性陷阱:next.config.js 里把reactStrictMode设为false,因为上传/下载组件都用useEffect监听 PeerJS 事件,严格模式的双调用会让连接建立两次——如果你按习惯改回true,会出现"握手两次、进度跳变"的诡异现象。

仓库证据:CLAUDE.md 明确写有Node.js (v18+)pnpm (preferred);package.json 的dev脚本就是nextdev:full则叠加 Redis 与 COTURN 容器用于完整 WebRTC 联调。

正确做法 + 验证

git clone https://gitcode.com/GitHub_Trending/fi/filepizza cd filepizza pnpm install pnpm dev

打开两个浏览器窗口(一个上传、一个用生成的短/长链接下载),能看到进度条与完成回调即视为本地跑通。若只验证信令链路,pnpm dev:full会拉起 docker-compose.yml 中的rediscoturn服务。

用环境变量接管存储与信令,避免默认值悄悄生效

现象与风险:FilePizza 的多个关键行为都由环境变量驱动,而默认值会"悄悄"回退到公共或本地服务。不配置REDIS_URL时会回退到进程内内存存储;不配COTURN_ENABLED时只走公共 STUN;PEERJS_HOST默认指向公共0.peerjs.com。上线后若沿用这些默认值,信令会打到公共 PeerJS 服务器、通道元数据也留在内存里,重启即丢。

仓库证据:默认值分散在 src/app/api/ice/route.ts(TURN_HOSTSTUN_SERVERPEERJS_HOST||兜底)与 src/redisClient.ts(process.env.REDIS_URL ? new Redis(...) : new Redis())。README.md 列出了完整变量表:REDIS_URLCOTURN_ENABLEDTURN_HOSTTURN_REALMSTUN_SERVERPEERJS_HOSTPEERJS_PATH

正确做法 + 验证:在你本地副本中按环境显式注入这些变量,而不是依赖兜底。启动后观察日志:getOrCreateChannelRepo会打印[ChannelRepo] Using Redis storageUsing in-memory storage,据此确认存储后端是否正确切换。生产环境建议自托管 PeerJS 并把PEERJS_HOST指向自己的域名,避免对外部公共节点产生运行时依赖。

收紧通道状态持久化与访问密钥

现象与风险:这是最容易被低估的一环。默认内存存储(MemoryChannelRepo)把通道放在Map里,进程一重启、多实例一部署,所有未完成的分享链接全部失效——这正是"游客态脆弱存储"的典型隐患。另一面是访问控制:通道靠secret做鉴权,renewChannel/destroyChannel都要求secret精确匹配,否则返回false。若把带secret的通道对象直接回传给前端,等于把"续期/删除权限"泄露给任意持有链接的人。

仓库证据:src/channel.ts 中MemoryChannelRepoRedisChannelRepo实现同一ChannelRepo接口,由getOrCreateChannelRepo()REDIS_URL二选一;deserializeChannel(str, scrubSecret)scrubSecret=true时会把secret置为undefined再返回,下载页取通道时应走这条脱敏路径。

正确做法 + 验证

// 下载/鉴权取通道时务必脱敏 const channel = await repo.fetchChannel(slug, true)

生产环境使用RedisChannelRepo,并在 docker-compose.production.yml 中把 Redis 端口绑定到127.0.0.1:6379(而非0.0.0.0),杜绝数据库对公网暴露。验证方式:拿到一个分享链接后,用一个错误的secret调用续期接口,应返回失败;用正确secret才能续期成功。

配好 coturn 与 TURN 凭据,让 NAT 后的设备能连通

现象与风险:纯 STUN 只能做打洞,双方都在严格 NAT 后会连接失败,必须依赖 TURN 中继。常见错误有两个:一是生产 compose 没放开 UDP 中继端口段,TURN 握手能过但媒体流建不起来;二是 TURN 凭据有效期与TURN_REALM不一致,导致coturn校验 HMAC 失败。

仓库证据:src/coturn.ts 的setTurnCredentialsmd5(username:realm:password)生成 HMAC 并以setex写入 Redis;src/app/api/ice/route.ts 每次生成临时username/password(TTL 24 小时),并返回turn:host:3478turns:host:5349两条 ICE 服务器。docker-compose.production.yml 额外打开了60000-60128/udp中继段,并设--min-port=60000 --max-port=60128,与COTURN_ENABLED=trueenv_file: .env配套。

正确做法 + 验证:在你本地副本的.env中补齐COTURN_ENABLED=trueTURN_HOSTTURN_REALM,并确认防火墙放行 UDP34785349与中继端口段。验证方式:运行pnpm dev:full后用两台不同网络(如手机热点 + 有线)互传文件,能稳定建连即说明 TURN 链路打通。

对齐通道结构与消息协议,让上传下载两端不"各说各话"

现象与风险:上传方与下载方之间靠一套 JSON 消息协议通信,任何一端字段名、枚举值对不上都不会报"接口错误",只会表现为传输卡住或进度不动。通道本身也是一个被严格约束的结构:短链接固定 8 位、长链接固定 4 个词,两端解析时若长度或字符集不一致就会取不到对应通道。

仓库证据:src/channel.ts 用 zod 的ChannelSchema约束{ secret?, longSlug, shortSlug, uploaderPeerID };src/slugs.ts 的generateShortSlug(8 个0-9a-z字符)与generateLongSlug(4 个词)定义了两端必须一致的 slug 形态;src/messages.ts 的MessageType枚举(RequestInfoInfoStartChunkChunkAckPauseDoneErrorPasswordRequiredUsePasswordReport)是上传/下载两端共享的"契约"。

正确做法 + 验证:修改消息字段时以ChannelSchemaMessageType为唯一事实来源,同步更新上传/下载两侧。验证方式:跑pnpm test:e2e(Playwright 的端到端用例会走真实消息协议),或在浏览器 DevTools 的 PeerJS 日志里确认RequestInfo → Info → Start → Chunk/ChunkAck → Done全序列出现。

用密码与密钥保护分享链接,替代"有链接即可下"

现象与风险:FilePizza 没有传统"管理员"概念,访问控制落在两件事上——可选的上传密码,以及通道secret。若未设密码,任何人拿到短/长链接即可下载;若secret被回传,陌生人就能续期或撤销链接。把密码校验当作"可选增强"而在生产里放任为空,会让敏感分享失去最后一道闸门。

仓库证据:src/messages.ts 定义了PasswordRequired/UsePassword两条消息,下载方据此提示并回填密码;src/channel.ts 中secretcrypto.randomUUID()生成、仅参与renew/destroy的鉴权,前端展示一律走scrubSecret脱敏。

正确做法 + 验证:在生产入口为需要保护的上传强制填写密码,并确认所有对外的通道返回都经过scrubSecret=true处理。验证方式:对一个设了密码的链接,在未输入密码前无法开始下载;再用脱敏后的通道对象(无secret)尝试续期,应被拒绝。

生产构建、流式下载验证与交付前检查清单

现象与风险output: 'standalone'的产物不会自动带上public/与静态资源,必须手动拷贝;而流式下载依赖的 Service Worker(StreamSaver)只在浏览器实际注册后生效,开发态测不出真实下载体验。跳过这一步,上线后可能遇到静态资源 404、大文件下载无法边下边写盘的问题。

仓库证据:package.json 的build脚本是next build && cp -r public .next/standalone/ && cp -r .next/static .next/standalone/.next/;Dockerfile 用deps → builder → runner三阶段,仅把publicstandalonestatic拷入最终镜像并以USER node运行server.js;public/sw.js 即 StreamSaver 的 Service Worker,负责把 WebRTC 数据流接到下载请求上。

正确做法 + 验证

pnpm build pnpm docker:build # 三阶段镜像 pnpm docker:up

构建后用一个大于内存缓冲的文件实测下载,确认能在浏览器中边收边写盘且进度连续,即验证了 Service Worker 流式下载链路。

交付前检查清单

阶段检查项仓库证据通过标准
运行时Node 18+ 且用 pnpm 安装CLAUDE.mdpnpm dev无依赖/版本报错
配置显式注入REDIS_URL/COTURN/PEERJSsrc/app/api/ice/route.ts日志出现Using Redis storage
状态安全走 Redis 且 Redis 仅绑本机docker-compose.production.yml错误secret无法续期
NAT 穿透放行 TURN 与 UDP 中继段src/coturn.ts跨网互传稳定建连
协议一致slug 与消息结构两端对齐src/slugs.ts、src/messages.tstest:e2e通过
访问控制密码 + 脱敏secretsrc/channel.ts无密码不可下载
交付独立构建 + 流式下载验证Dockerfile、public/sw.js大文件边下边写盘 🚀

按这条流水线逐项过完,你就把 FilePizza 从一个本地 Demo 推进到了一个信令可控、状态持久、NAT 可穿透、链接受保护、下载可验证的可交付点对点文件传输服务。🔧

【免费下载链接】filepizza:pizza: Peer-to-peer file transfers in your browser项目地址: https://gitcode.com/GitHub_Trending/fi/filepizza

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询