阅读视图

发现新文章,点击刷新页面。

🚀 纯前端离线局域网P2P大文件断点传输:别让你的照片墙崩了

前言:小明和他的照片墙危机

想象一下,你是小明,一个热爱摄影的程序员。周末去爬山拍了一堆4K高清照片,总共3GB,准备发给老婆分享。微信传?算了,压缩后画质渣得像像素风。网盘?离线状态下连不上。蓝牙?慢得像蜗牛在跑步。你急得团团转,突然灵机一动:用电脑直接传啊!可是怎么在浏览器里实现局域网P2P大文件传输,还得支持断点续传?别急,今天咱们就聊聊这个技术方案。

技术背景:P2P不是什么新鲜玩意儿

P2P(点对点)传输在局域网里其实挺常见的,BT下载就是经典案例。但咱们今天聊的是纯前端实现,意思是完全不用后端服务器,用户A直接把文件传给用户B。核心技术是WebRTC(Web Real-Time Communication),这货本来是用来视频聊天的,但咱们可以拿来传文件。

为什么选WebRTC?因为它支持数据通道(DataChannel),可以直接在浏览器间建立连接。加上File API和Blob,咱们就能把大文件切成小块,边传边收,断点续传自然就出来了。

核心实现:分块传输 + 断点续传

1. 文件切块:大象塞冰箱,得先切成块

浏览器处理大文件有个硬伤:内存限制。如果直接把3GB文件读进内存,Chrome得哭爹喊娘。所以咱们用FileReader分块读:

// 文件分块函数
function chunkFile(file, chunkSize = 1024 * 1024) { // 1MB每块
  const chunks = [];
  let offset = 0;
  
  while (offset < file.size) {
    const chunk = file.slice(offset, offset + chunkSize);
    chunks.push(chunk);
    offset += chunkSize;
  }
  
  return chunks;
}

这里用file.slice()切块,每个块1MB。为什么要1MB?平衡传输效率和内存占用,太小网络开销大,太大浏览器卡。

2. WebRTC连接:建立地下通道

WebRTC连接需要信令服务器(用来交换连接信息),但咱们是离线局域网,所以可以用WebSocket或者直接用浏览器本地存储交换SDP(会话描述协议)。

// 创建RTCPeerConnection
const pc = new RTCPeerConnection({
  iceServers: [] // 局域网不需要STUN服务器
});

// 创建数据通道
const dataChannel = pc.createDataChannel('file-transfer');

// 监听连接事件
dataChannel.onopen = () => console.log('通道开了,可以传文件了');
dataChannel.onmessage = handleMessage;

3. 断点续传:从中断处继续

断点续传的关键是记录已传块的进度。用IndexedDB或者localStorage存进度:

// 发送文件块
async function sendFileChunks(file, dataChannel) {
  const chunks = chunkFile(file);
  const progress = loadProgress(file.name) || 0; // 从本地加载进度
  
  for (let i = progress; i < chunks.length; i++) {
    const chunk = chunks[i];
    const arrayBuffer = await chunk.arrayBuffer();
    
    // 发送块数据,带上索引
    dataChannel.send(JSON.stringify({
      type: 'chunk',
      index: i,
      data: arrayBuffer
    }));
    
    saveProgress(file.name, i + 1); // 保存进度
  }
  
  // 发送结束信号
  dataChannel.send(JSON.stringify({ type: 'end' }));
}

接收端收到块后,先存到临时数组,收到'end'信号再合并成完整文件。

浏览器限制:那些坑爹的现实

1. 文件大小限制:Chrome说不行就不行

Chrome对单个文件上传有限制,默认是2GB。有些版本甚至更低。遇到大文件怎么办?继续分块,但块数太多会影响性能。

解决方案:用File System Access API(Chrome 86+),可以直接操作本地文件系统,绕过内存限制。

// 使用File System Access API
const fileHandle = await window.showOpenFilePicker();
const file = await fileHandle.getFile();
const writableStream = await fileHandle.createWritable();

// 分块写入
for (const chunk of chunks) {
  await writableStream.write(chunk);
}
await writableStream.close();

2. 内存泄漏:传着传着浏览器崩了

大文件传输时,如果不及时释放Blob对象,内存会爆。解决方案:用stream API边读边传:

// 用ReadableStream处理大文件
const stream = file.stream();
const reader = stream.getReader();

while (true) {
  const { done, value } = await reader.read();
  if (done) break;
  
  // 直接发送value(Uint8Array)
  dataChannel.send(value);
}

3. 网络限制:局域网防火墙挡道

公司局域网可能有防火墙,WebRTC的UDP连接会被挡。解决方案:降级到WebSocket,或者用TURN服务器中转(但这就不纯前端了)。

实战案例:三个真实场景

案例1:照片分享应用

我之前做的一个家庭相册App,用这个技术实现了局域网照片同步。妈妈在客厅电脑上传相册,爸爸在卧室就能直接收到,不用连路由器。

关键代码:进度条显示 + 错误重试

// 进度显示
dataChannel.onmessage = (event) => {
  const message = JSON.parse(event.data);
  if (message.type === 'progress') {
    updateProgressBar(message.percent);
  }
};

案例2:游戏存档同步

做游戏开发时,团队成员在局域网同步大存档文件(几GB)。用断点续传,网络断了重连后从断点继续,省去了重传的时间。

案例3:视频剪辑素材传输

剪辑师在局域网传4K视频素材。传统方法用U盘慢,用这个方案直接浏览器传,还能显示传输速度和剩余时间。

其他方案:当WebRTC不够用时

方案1:WebTorrent

基于WebRTC的BitTorrent实现,支持多对多传输,更适合大文件群发。

import WebTorrent from 'webtorrent';

const client = new WebTorrent();
client.seed(file, (torrent) => {
  console.log('种子创建成功:', torrent.magnetURI);
});

优点:多人同时下载快。缺点:需要种子文件管理。

方案2:Socket.IO + 二进制传输

用WebSocket传二进制数据,配合socket.io实现断点续传。

const socket = io();
socket.emit('send-file-chunk', { chunk, index });

socket.on('chunk-received', (index) => {
  // 继续发下一块
});

优点:兼容性好。缺点:需要服务器中转。

方案3:Electron应用

如果纯浏览器限制太多,可以做个Electron桌面应用,用Node.js的fs模块直接操作文件系统,结合WebRTC。

总结与展望

纯前端P2P大文件断点传输,听起来高大上,其实就是把文件切块 + WebRTC传数据 + 本地存储进度。浏览器限制是客观存在的,但通过File System API和内存管理,大部分场景都能搞定。

未来,随着WebTransport协议的普及(基于HTTP/3),传输效率会更高。5G和WiFi 6普及后,局域网传输速度会飞起。

下次老婆让你传照片,别再抱怨网速了,直接用浏览器P2P传吧!有什么问题,评论区见。🚀

❌