前言:小明和他的照片墙危机
想象一下,你是小明,一个热爱摄影的程序员。周末去爬山拍了一堆4K高清照片,总共3GB,准备发给老婆分享。微信传?算了,压缩后画质渣得像像素风。网盘?离线状态下连不上。蓝牙?慢得像蜗牛在跑步。你急得团团转,突然灵机一动:用电脑直接传啊!可是怎么在浏览器里实现局域网P2P大文件传输,还得支持断点续传?别急,今天咱们就聊聊这个技术方案。
技术背景:P2P不是什么新鲜玩意儿
P2P(点对点)传输在局域网里其实挺常见的,BT下载就是经典案例。但咱们今天聊的是纯前端实现,意思是完全不用后端服务器,用户A直接把文件传给用户B。核心技术是WebRTC(Web Real-Time Communication),这货本来是用来视频聊天的,但咱们可以拿来传文件。
为什么选WebRTC?因为它支持数据通道(DataChannel),可以直接在浏览器间建立连接。加上File API和Blob,咱们就能把大文件切成小块,边传边收,断点续传自然就出来了。
核心实现:分块传输 + 断点续传
1. 文件切块:大象塞冰箱,得先切成块
浏览器处理大文件有个硬伤:内存限制。如果直接把3GB文件读进内存,Chrome得哭爹喊娘。所以咱们用FileReader分块读:
1// 文件分块函数 2function chunkFile(file, chunkSize = 1024 * 1024) { // 1MB每块 3 const chunks = []; 4 let offset = 0; 5 6 while (offset < file.size) { 7 const chunk = file.slice(offset, offset + chunkSize); 8 chunks.push(chunk); 9 offset += chunkSize; 10 } 11 12 return chunks; 13} 14
这里用file.slice()切块,每个块1MB。为什么要1MB?平衡传输效率和内存占用,太小网络开销大,太大浏览器卡。
2. WebRTC连接:建立地下通道
WebRTC连接需要信令服务器(用来交换连接信息),但咱们是离线局域网,所以可以用WebSocket或者直接用浏览器本地存储交换SDP(会话描述协议)。
1// 创建RTCPeerConnection 2const pc = new RTCPeerConnection({ 3 iceServers: [] // 局域网不需要STUN服务器 4}); 5 6// 创建数据通道 7const dataChannel = pc.createDataChannel('file-transfer'); 8 9// 监听连接事件 10dataChannel.onopen = () => console.log('通道开了,可以传文件了'); 11dataChannel.onmessage = handleMessage; 12
3. 断点续传:从中断处继续
断点续传的关键是记录已传块的进度。用IndexedDB或者localStorage存进度:
1// 发送文件块 2async function sendFileChunks(file, dataChannel) { 3 const chunks = chunkFile(file); 4 const progress = loadProgress(file.name) || 0; // 从本地加载进度 5 6 for (let i = progress; i < chunks.length; i++) { 7 const chunk = chunks[i]; 8 const arrayBuffer = await chunk.arrayBuffer(); 9 10 // 发送块数据,带上索引 11 dataChannel.send(JSON.stringify({ 12 type: 'chunk', 13 index: i, 14 data: arrayBuffer 15 })); 16 17 saveProgress(file.name, i + 1); // 保存进度 18 } 19 20 // 发送结束信号 21 dataChannel.send(JSON.stringify({ type: 'end' })); 22} 23
接收端收到块后,先存到临时数组,收到'end'信号再合并成完整文件。
浏览器限制:那些坑爹的现实
1. 文件大小限制:Chrome说不行就不行
Chrome对单个文件上传有限制,默认是2GB。有些版本甚至更低。遇到大文件怎么办?继续分块,但块数太多会影响性能。
解决方案:用File System Access API(Chrome 86+),可以直接操作本地文件系统,绕过内存限制。
1// 使用File System Access API 2const fileHandle = await window.showOpenFilePicker(); 3const file = await fileHandle.getFile(); 4const writableStream = await fileHandle.createWritable(); 5 6// 分块写入 7for (const chunk of chunks) { 8 await writableStream.write(chunk); 9} 10await writableStream.close(); 11
2. 内存泄漏:传着传着浏览器崩了
大文件传输时,如果不及时释放Blob对象,内存会爆。解决方案:用stream API边读边传:
1// 用ReadableStream处理大文件 2const stream = file.stream(); 3const reader = stream.getReader(); 4 5while (true) { 6 const { done, value } = await reader.read(); 7 if (done) break; 8 9 // 直接发送value(Uint8Array) 10 dataChannel.send(value); 11} 12
3. 网络限制:局域网防火墙挡道
公司局域网可能有防火墙,WebRTC的UDP连接会被挡。解决方案:降级到WebSocket,或者用TURN服务器中转(但这就不纯前端了)。
实战案例:三个真实场景
案例1:照片分享应用
我之前做的一个家庭相册App,用这个技术实现了局域网照片同步。妈妈在客厅电脑上传相册,爸爸在卧室就能直接收到,不用连路由器。
关键代码:进度条显示 + 错误重试
1// 进度显示 2dataChannel.onmessage = (event) => { 3 const message = JSON.parse(event.data); 4 if (message.type === 'progress') { 5 updateProgressBar(message.percent); 6 } 7}; 8
案例2:游戏存档同步
做游戏开发时,团队成员在局域网同步大存档文件(几GB)。用断点续传,网络断了重连后从断点继续,省去了重传的时间。
案例3:视频剪辑素材传输
剪辑师在局域网传4K视频素材。传统方法用U盘慢,用这个方案直接浏览器传,还能显示传输速度和剩余时间。
其他方案:当WebRTC不够用时
方案1:WebTorrent
基于WebRTC的BitTorrent实现,支持多对多传输,更适合大文件群发。
1import WebTorrent from 'webtorrent'; 2 3const client = new WebTorrent(); 4client.seed(file, (torrent) => { 5 console.log('种子创建成功:', torrent.magnetURI); 6}); 7
优点:多人同时下载快。缺点:需要种子文件管理。
方案2:Socket.IO + 二进制传输
用WebSocket传二进制数据,配合socket.io实现断点续传。
1const socket = io(); 2socket.emit('send-file-chunk', { chunk, index }); 3 4socket.on('chunk-received', (index) => { 5 // 继续发下一块 6}); 7
优点:兼容性好。缺点:需要服务器中转。
方案3:Electron应用
如果纯浏览器限制太多,可以做个Electron桌面应用,用Node.js的fs模块直接操作文件系统,结合WebRTC。
总结与展望
纯前端P2P大文件断点传输,听起来高大上,其实就是把文件切块 + WebRTC传数据 + 本地存储进度。浏览器限制是客观存在的,但通过File System API和内存管理,大部分场景都能搞定。
未来,随着WebTransport协议的普及(基于HTTP/3),传输效率会更高。5G和WiFi 6普及后,局域网传输速度会飞起。
下次老婆让你传照片,别再抱怨网速了,直接用浏览器P2P传吧!有什么问题,评论区见。🚀
《🚀 纯前端离线局域网P2P大文件断点传输:别让你的照片墙崩了》 是转载文章,点击查看原文。