You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

在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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.26 03:07:01