如何在 JavaScript 中检测互联网速度?

IT技术 javascript download-speed
2021-02-03 13:49:48

如何创建一个 JavaScript 页面来检测用户的互联网速度并将其显示在页面上?类似“你的网速是 ??/?? 千字节/秒”

6个回答

这在某种程度上是可能的,但不会非常准确,这个想法是加载具有已知文件大小的图像,然后在其onload事件中测量触发该事件之前经过的时间,并将这段时间除以图像文件大小。

示例可以在这里找到:Calculate speed using javascript

应用此处建议的修复程序的测试用例:

//JUST AN EXAMPLE, PLEASE USE YOUR OWN PICTURE!
var imageAddr = "http://www.kenrockwell.com/contax/images/g2/examples/31120037-5mb.jpg"; 
var downloadSize = 4995374; //bytes

function ShowProgressMessage(msg) {
    if (console) {
        if (typeof msg == "string") {
            console.log(msg);
        } else {
            for (var i = 0; i < msg.length; i++) {
                console.log(msg[i]);
            }
        }
    }
    
    var oProgress = document.getElementById("progress");
    if (oProgress) {
        var actualHTML = (typeof msg == "string") ? msg : msg.join("<br />");
        oProgress.innerHTML = actualHTML;
    }
}

function InitiateSpeedDetection() {
    ShowProgressMessage("Loading the image, please wait...");
    window.setTimeout(MeasureConnectionSpeed, 1);
};    

if (window.addEventListener) {
    window.addEventListener('load', InitiateSpeedDetection, false);
} else if (window.attachEvent) {
    window.attachEvent('onload', InitiateSpeedDetection);
}

function MeasureConnectionSpeed() {
    var startTime, endTime;
    var download = new Image();
    download.onload = function () {
        endTime = (new Date()).getTime();
        showResults();
    }
    
    download.onerror = function (err, msg) {
        ShowProgressMessage("Invalid image, or error downloading");
    }
    
    startTime = (new Date()).getTime();
    var cacheBuster = "?nnn=" + startTime;
    download.src = imageAddr + cacheBuster;
    
    function showResults() {
        var duration = (endTime - startTime) / 1000;
        var bitsLoaded = downloadSize * 8;
        var speedBps = (bitsLoaded / duration).toFixed(2);
        var speedKbps = (speedBps / 1024).toFixed(2);
        var speedMbps = (speedKbps / 1024).toFixed(2);
        ShowProgressMessage([
            "Your connection speed is:", 
            speedBps + " bps", 
            speedKbps + " kbps", 
            speedMbps + " Mbps"
        ]);
    }
}
<h1 id="progress">JavaScript is turned off, or your browser is realllllly slow</h1>

“真实”速度测试服务的快速比较显示,在使用大图片时,只有 0.12 Mbps 的微小差异。

为确保测试的完整性,您可以在启用 Chrome 开发工具节流的情况下运行代码,然后查看结果是否符合限制。(归功于user284130 :))

要记住的重要事项:

  1. 正在使用的图像应适当优化和压缩。如果不是,则 Web 服务器对连接的默认压缩可能会显示比实际速度更大的速度。另一种选择是使用不可压缩的文件格式,例如 jpg。(感谢 Rauli Rajande指出这一点和 Fluxine提醒我

  2. 上面描述的缓存破坏器机制可能不适用于某些 CDN 服务器,这些服务器可以配置为忽略查询字符串参数,因此更好地在图像本身上设置缓存控制标头。(感谢 orcaman指出这一点

  3. 图片尺寸越大越好。更大的图像会使测试更准确,5 mb 是不错的,但是如果您可以使用更大的图像会更好。

@AndrewSchultz 是的,可能是这样。速度测试站点通常足够大,可以在世界各地拥有多台服务器,并使用离用户最近的服务器。此外,可能是托管您在我的代码中使用的文件的服务器的上传限制。
2021-03-12 13:49:48
注意测试图像是否经过适当优化和压缩。如果不是,则网络服务器对连接的默认压缩可能会显示比实际速度更大的速度。
2021-03-17 13:49:48
@Dilip 较小的图像意味着不太准确的测试,它故意很大。:)
2021-03-25 13:49:48
我发现了一个小技巧来确保您的图像适合测试:在启用 Chrome 开发工具节流的情况下运行代码,然后查看结果是否符合限制。希望这可以帮助某人。
2021-04-02 13:49:48
加入 Rauli Rajande :最好使用不可压缩(或几乎)的文件,否则网络服务器压缩module可能会显着减少它,从而使该措施无效。jpeg 图像将是一个不错的选择。
2021-04-06 13:49:48

好吧,这是 2017 年,所以您现在拥有网络信息 API(尽管目前对浏览器的支持有限)来获取某种估计的下行速度信息:

navigator.connection.downlink

这是以每秒 Mbits 为单位的有效带宽估计。浏览器根据最近观察到的跨最近活动连接的应用层吞吐量进行此估计。毋庸置疑,这种方法的最大优点是您无需下载任何内容仅用于带宽/速度计算。

您可以在此处查看此内容和其他一些相关属性

由于它的支持有限和跨浏览器的不同实现(截至 2017 年 11 月),强烈建议详细阅读此内容

我可以使用中的红色太多了!
2021-03-21 13:49:48
@Tobi Me 都没有,如果速度超过 10Mb,我会继续阅读 10
2021-03-22 13:49:48
@Tobi Chrome 将最大值设置为 10 兆位以停止指纹尝试。
2021-03-22 13:49:48
@Tobi 我似乎也没有超过 10MBit,应该更像是 100MBit
2021-04-03 13:49:48
使用它我没有得到高于 10MBit 的数字。有限制吗?
2021-04-04 13:49:48

我需要一种快速的方法来确定用户连接速度是否足够快以启用/禁用我正在处理的站点中的某些功能,我制作了这个小脚本来平均下载单个(小)图像所需的时间很多次,它在我的测试中工作得非常准确,例如能够清楚地区分 3G 或 Wi-Fi,也许有人可以制作更优雅的版本甚至 jQuery 插件。

var arrTimes = [];
var i = 0; // start
var timesToTest = 5;
var tThreshold = 150; //ms
var testImage = "http://www.google.com/images/phd/px.gif"; // small image in your server
var dummyImage = new Image();
var isConnectedFast = false;

testLatency(function(avg){
  isConnectedFast = (avg <= tThreshold);
  /** output */
  document.body.appendChild(
    document.createTextNode("Time: " + (avg.toFixed(2)) + "ms - isConnectedFast? " + isConnectedFast)
  );
});

/** test and average time took to download image from server, called recursively timesToTest times */
function testLatency(cb) {
  var tStart = new Date().getTime();
  if (i<timesToTest-1) {
    dummyImage.src = testImage + '?t=' + tStart;
    dummyImage.onload = function() {
      var tEnd = new Date().getTime();
      var tTimeTook = tEnd-tStart;
      arrTimes[i] = tTimeTook;
      testLatency(cb);
      i++;
    };
  } else {
    /** calculate average of array items then callback */
    var sum = arrTimes.reduce(function(a, b) { return a + b; });
    var avg = sum / arrTimes.length;
    cb(avg);
  }
}

上传测试呢?
2021-03-14 13:49:48

正如我在 StackOverflow 上的另一个答案中概述的那样,您可以通过定时下载各种大小的文件(从小处开始,如果连接似乎允许则增加)来做到这一点,确保通过缓存头等文件是真的正在从远程服务器读取而不是从缓存中检索。这不一定要求您拥有自己的服务器(文件可能来自S3或类似文件),但您需要从某个地方获取文件以测试连接速度。

也就是说,时间点带宽测试是出了名的不可靠,因为它们会受到其他窗口中下载的其他项目、服务器速度、途中链接等的影响。但是您可以大致了解一下使用这种技术。

@Jakub:几种方式中的任何一种。例如,如果您向 hidden 提交表单iframe,则轮询iframe或 cookie 以完成。如果您使用XMLHttpRequest对象进行发布,则会有一个完成回调。
2021-03-21 13:49:48
那么你怎么知道上传是什么时候完成的呢?
2021-03-27 13:49:48
@Jakub:您必须有一个可以上传的地方,但没有理由不能为此使用相同的技术。您可以使用即时生成的数据,当然,您也可以重新使用下载的某些数据进行下载测试。
2021-04-03 13:49:48

图像技巧很酷,但在我的测试中,它是在我想要完成的一些 ajax 调用之前加载的。

2017 年的正确解决方案是使用工人 ( http://caniuse.com/#feat=webworkers )。

工人看起来像:

/**
 * This function performs a synchronous request
 * and returns an object contain informations about the download
 * time and size
 */
function measure(filename) {
  var xhr = new XMLHttpRequest();
  var measure = {};
  xhr.open("GET", filename + '?' + (new Date()).getTime(), false);
  measure.start = (new Date()).getTime();
  xhr.send(null);
  measure.end = (new Date()).getTime();
  measure.len = parseInt(xhr.getResponseHeader('Content-Length') || 0);
  measure.delta = measure.end - measure.start;
  return measure;
}

/**
 * Requires that we pass a base url to the worker
 * The worker will measure the download time needed to get
 * a ~0KB and a 100KB.
 * It will return a string that serializes this informations as
 * pipe separated values
 */
onmessage = function(e) {
  measure0 = measure(e.data.base_url + '/test/0.bz2');
  measure100 = measure(e.data.base_url + '/test/100K.bz2');
  postMessage(
    measure0.delta + '|' +
    measure0.len + '|' +
    measure100.delta + '|' +
    measure100.len
  );
};

将调用 Worker 的 js 文件:

var base_url = PORTAL_URL + '/++plone++experimental.bwtools';
if (typeof(Worker) === 'undefined') {
  return; // unsupported
}
w = new Worker(base_url + "/scripts/worker.js");
w.postMessage({
  base_url: base_url
});
w.onmessage = function(event) {
  if (event.data) {
    set_cookie(event.data);
  }
};

从我写的 Plone 包中提取的代码: