基于PeerJS的React安卓端文件传输速度过慢问题求助
安卓设备上PeerJS文件传输速度过慢的优化方案
我开发了一个React应用,通过PeerJS实现跨设备文件传输,但安卓设备上的文件传输速度过慢。项目通过二维码获取双方的Peer ID。以下是原发送和接收代码:
原发送端代码
useEffect(() => { var peer = new Peer(); peer.on("open", function (id) { console.log(id); }); peerRef.current = peer; }, []); const sendFiles = () => { var conn = peerRef.current.connect(`${receiverId}`); if (files != null) { conn.on("open", () => { for (let j = 0; j < files.length; j++) { const chunkSize = 1024 * 1024; // In bytes const chunks = Math.ceil(files[j].size / chunkSize); console.log(files[j].size); var pro = 0; for (let i = 0; i < chunks; i++) { const offset = i * chunkSize; pro = ((i + 1) / chunks) * 100; setProgress(pro); conn.send({ file: files[j].slice(offset, offset + chunkSize), name: files[j].name, size: files[j].size, type: "file", progress: ((i + 1) / chunks) * 100, }); } } }); } };
原接收端代码
useEffect(() => { var peer = new Peer(); peer.on("open", function (id) { setReceiverID(id); console.log(id); }); peer.on("connection", (conn) => { var chunk = []; conn.on("data", (data) => { if (data) { setReceiving(true); } chunk = [...chunk, data.file]; setProgress(data.progress); if (data.progress == 100) { console.log(data); combineTheChunks(chunk); chunk = []; setProgress(0); setReceivedName((prevname) => [...prevname, data.name]); } }); }); }, []);
问题分析
原代码存在几个导致安卓端传输缓慢的核心问题:
- 同步密集发送Chunk:同步循环发送所有Chunk,没有考虑安卓设备的网络处理能力,导致数据拥塞,PeerJS无法及时处理所有数据包。
- 过频繁的状态更新:每次发送Chunk都调用
setProgress,触发React组件频繁重渲染,占用设备资源。 - 低效的Chunk存储:接收端用
chunk = [...chunk, data.file]创建新数组存储Chunk,频繁的数组拷贝会消耗内存和CPU。 - Peer实例未优化:默认Peer配置可能没有针对移动端网络做适配,比如ICE服务器、带宽限制等。
优化方案及修改后代码
1. 异步发送+接收确认
通过等待接收端的确认消息控制发送节奏,避免安卓端网络拥塞。
2. 增大Chunk Size
将Chunk从1MB提升至2-4MB,减少发送次数,降低协议开销。
3. 减少状态更新频率
仅在进度明显变化时更新进度状态,避免不必要的重渲染。
4. 优化Chunk存储
用Map按文件名存储Chunk数组,按索引直接赋值,避免数组拷贝和顺序问题。
5. Peer配置优化
添加STUN服务器配置,指定WebRTC语义,提升移动端连接质量。
修改后的发送端代码
useEffect(() => { const peer = new Peer({ config: { iceServers: [{ urls: 'stun:stun.l.google.com:19302' }], sdpSemantics: 'unified-plan' } }); peer.on("open", (id) => console.log(id)); peerRef.current = peer; // 组件卸载时销毁Peer,避免内存泄漏 return () => peer.destroy(); }, []); const sendFiles = async () => { const conn = peerRef.current.connect(`${receiverId}`); if (!files) return; conn.on("open", async () => { // 封装等待接收端确认的方法 const waitForAck = () => new Promise(resolve => { conn.once("data", (ack) => ack.type === "ack" && resolve()); }); for (const file of files) { const chunkSize = 2 * 1024 * 1024; // 2MB Chunk const totalChunks = Math.ceil(file.size / chunkSize); let currentProgress = 0; for (let i = 0; i < totalChunks; i++) { const offset = i * chunkSize; const chunk = file.slice(offset, offset + chunkSize); currentProgress = Math.round(((i + 1) / totalChunks) * 100); // 每1%更新一次进度,减少重渲染 if (currentProgress % 1 === 0) { setProgress(currentProgress); } // 发送Chunk及元数据 conn.send({ file: chunk, name: file.name, size: file.size, type: "file", chunkIndex: i, totalChunks, progress: currentProgress }); // 等待接收端确认后再发送下一个Chunk await waitForAck(); } } }); };
修改后的接收端代码
useEffect(() => { const peer = new Peer({ config: { iceServers: [{ urls: 'stun:stun.l.google.com:19302' }], sdpSemantics: 'unified-plan' } }); peer.on("open", (id) => { setReceiverID(id); console.log(id); }); peer.on("connection", (conn) => { // 用Map存储多文件的Chunk,支持并发传输 const fileChunkMap = new Map(); conn.on("data", (data) => { if (data.type === "file") { setReceiving(true); // 初始化当前文件的Chunk数组 if (!fileChunkMap.has(data.name)) { fileChunkMap.set(data.name, new Array(data.totalChunks)); } const chunks = fileChunkMap.get(data.name); chunks[data.chunkIndex] = data.file; // 按索引直接存储,保证顺序 // 减少进度更新频率 const currentProgress = data.progress; if (currentProgress % 1 === 0) { setProgress(currentProgress); } // 发送确认消息,通知发送端可以继续发送下一个Chunk conn.send({ type: "ack" }); // 所有Chunk接收完成,合并文件 if (currentProgress === 100) { const allChunks = fileChunkMap.get(data.name); combineTheChunks(allChunks, data.name); fileChunkMap.delete(data.name); setProgress(0); setReceivedName(prev => [...prev, data.name]); } } }); }); return () => peer.destroy(); }, []);
额外优化建议
- 如果传输大文件,可考虑添加带宽检测,动态调整Chunk大小和发送速率。
- 若存在NAT穿透问题,添加TURN服务器配置,提升安卓设备在复杂网络环境下的连接成功率。
- 接收端的
combineTheChunks方法可优化为使用Blob直接合并,避免内存溢出:const combineTheChunks = (chunks, fileName) => { const blob = new Blob(chunks); // 后续处理Blob(如下载、存储等) };
内容的提问来源于stack exchange,提问作者Kamal
相关产品推荐
相关产品推荐

