在ElectronJS/NodeJS中实现类似Speedtest.net的精准网速检测方案
更精准的公网上下行网速检测实现方案
原有方案的核心问题
- 依赖预设文件大小计算,但实际下载的文件可能因CDN压缩、缓存等和预设值不符,误差大
- 单次测试样本太少,网络波动直接影响结果
- 没排除TCP握手、SSL协商的时间,这些耗时会被算入下载时间,拉低精度
- 仅支持下载测速,完全没覆盖上传检测
改进的下载测速实现
用performance.now()(微秒级精度,不受系统时间影响)替代Date.getTime(),多次测试并跳过首次连接耗时,同时获取真实下载字节数,避免预设值误差。
// 下载测速:多次请求大文件,排除首次连接耗时取平均 async function measureDownloadSpeed(testUrl: string, testCount = 3): Promise<number> { let totalBytes = 0; let totalTime = 0; for (let i = 0; i < testCount; i++) { // 缓存破击+强制不缓存,避免浏览器复用缓存 const url = `${testUrl}?cache_bust=${Date.now() + Math.random()}`; const startTime = performance.now(); try { const response = await fetch(url, { cache: 'no-store' }); if (!response.ok) throw new Error(`HTTP错误: ${response.status}`); // 读取全部响应内容,确保下载完成 const blob = await response.blob(); const endTime = performance.now(); // 第一次测试包含连接建立时间,不计入平均 if (i > 0) { totalBytes += blob.size; totalTime += (endTime - startTime); } } catch (err) { console.error(`第${i+1}次下载测试失败:`, err); throw err; } } if (totalTime === 0) throw new Error("有效测试次数不足"); // 转成Mbps:字节数*8(转比特) / 时间(转秒) / 1e6(转Mbps) const speedMbps = (totalBytes * 8) / (totalTime / 1000) / 1e6; return parseFloat(speedMbps.toFixed(2)); }
上传测速实现
通过生成大体积Blob数据上传到服务器(需后端配合接收接口),同样多次测试取平均,排除首次连接耗时。
// 上传测速:生成大Blob,多次上传取平均 async function measureUploadSpeed(uploadUrl: string, testSizeMB = 5, testCount = 2): Promise<number> { // 生成指定大小的测试Blob(填充随机数据) const blobSize = testSizeMB * 1024 * 1024; const arrayBuffer = new ArrayBuffer(blobSize); new Uint8Array(arrayBuffer).fill(0x41); // 填充'A'字符构造大文件 const testBlob = new Blob([arrayBuffer], { type: 'application/octet-stream' }); let totalBytes = 0; let totalTime = 0; for (let i = 0; i < testCount; i++) { const startTime = performance.now(); try { const response = await fetch(uploadUrl, { method: 'POST', body: testBlob, headers: { 'Content-Type': 'application/octet-stream' }, cache: 'no-store' }); if (!response.ok) throw new Error(`HTTP错误: ${response.status}`); const endTime = performance.now(); // 跳过第一次上传的连接耗时 if (i > 0) { totalBytes += blobSize; totalTime += (endTime - startTime); } } catch (err) { console.error(`第${i+1}次上传测试失败:`, err); throw err; } } if (totalTime === 0) throw new Error("有效测试次数不足"); const speedMbps = (totalBytes * 8) / (totalTime / 1000) / 1e6; return parseFloat(speedMbps.toFixed(2)); }
精度优化的关键细节
- 测试文件选5-20MB:太小误差大,太大耗时久,平衡用户体验和精度
- 用同区域CDN/服务器:跨区域的网络延迟会干扰测速结果,尽量选用户就近的节点
- 实时显示中间结果:多次测试时可以展示每一次的速度,让用户感知进度
- 处理异常情况:比如网络中断、服务器错误,给用户明确提示
组合使用示例
// 完整测速流程 async function runFullSpeedTest() { try { // 替换为你的大文件测试地址(建议用同区域CDN) const downloadTestUrl = 'https://your-cdn-domain.com/large-test-file.bin'; // 替换为你的后端上传测试接口 const uploadTestUrl = 'https://your-server-domain.com/upload-test'; console.log("正在测试下载速度..."); const downloadSpeed = await measureDownloadSpeed(downloadTestUrl); console.log(`下载速度: ${downloadSpeed} Mbps`); console.log("正在测试上传速度..."); const uploadSpeed = await measureUploadSpeed(uploadTestUrl); console.log(`上传速度: ${uploadSpeed} Mbps`); return { downloadSpeed, uploadSpeed }; } catch (err) { console.error("测速失败:", err); throw err; } }
内容的提问来源于stack exchange,提问作者Elabbasy00
相关产品推荐
相关产品推荐

