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

Sha*_*our 200 javascript download-speed

如何创建一个能够检测用户上网速度并在页面上显示的JavaScript页面?像"你的网速是?? ?? ?? Kb/s".

Sha*_*ard 279

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

可以在此处找到示例:使用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"
        ]);
    }
}
Run Code Online (Sandbox Code Playgroud)
<h1 id="progress">JavaScript is turned off, or your browser is realllllly slow</h1>
Run Code Online (Sandbox Code Playgroud)

"真实"速度测试服务的快速比较在使用大图时显示出0.12 Mbps的小差异.

为确保测试的完整性,您可以在启用Chrome开发工具限制的情况下运行代码,然后查看结果是否与限制匹配.(信用转到user284130 :))

要记住的重要事项:

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

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

  • 注意测试图像已正确优化和压缩.如果不是,则网络服务器对连接的默认压缩可能会显示比实际更大的速度. (7认同)
  • 我找到了一个小技巧来确保您的图像适合测试:在启用Chrome开发工具限制的情况下运行代码,并查看结果是否与限制相符.希望这可以帮助某人. (3认同)
  • 加入Rauli Rajande:更好地使用不可压缩(或接近)的文件,或者webserver压缩模块可能会显着减少它,使测量无效.jpeg图像将是一个不错的选择. (3认同)
  • @Dilip较小的图像表示较不精确的测试,这是故意的。:) (2认同)
  • @AndrewSchultz 是的,可能是这样。速度测试站点通常足够大,可以在世界各地拥有多台服务器,并使用离用户最近的服务器。此外,可能是托管您在我的代码中使用的文件的服务器的上传限制。 (2认同)

Pun*_*t S 60

好吧,这是2017年,所以你现在有了网络信息API(尽管目前在浏览器上有限的支持),以获得某种估计下行链路速度信息:

navigator.connection.downlink
Run Code Online (Sandbox Code Playgroud)

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

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

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

  • 这是我可以使用的很多红色! (15认同)
  • @Tobi Chrome 将最大值设置为 10 MB 以阻止指纹识别尝试。 (7认同)
  • 使用它我没有得到高于 10MBit 的数字。有限制吗? (3认同)

T.J*_*der 20

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

也就是说,时间点带宽测试是众所周知的不可靠,因为它们受到在其他窗口中下载的其他项目,服务器的速度,途中的链接等等的影响.但是你可以得到一个粗略的想法使用这种技术.

  • @Jakub:几种方法中的任何一种。例如,如果您向隐藏的“ iframe”提交表单,则会轮询“ iframe”或Cookie以完成操作。如果您使用XMLHttpRequest对象进行发布,则会有一个回调来完成。 (2认同)

小智 16

我需要一种快速的方法来确定用户连接速度是否足够快以启用/禁用我正在处理的站点中的某些功能,我制作了这个小脚本来平均下载单个(小)图像所需的时间很多次,它在我的测试中非常准确地工作,能够清楚地区分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);
  }
}
Run Code Online (Sandbox Code Playgroud)

  • 上传测试怎么样? (2认同)

ale*_*isa 9

图像技巧很酷,但在我的测试中,它是在我希望完成一些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
  );
};
Run Code Online (Sandbox Code Playgroud)

将调用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);
  }
};
Run Code Online (Sandbox Code Playgroud)

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


Meh*_*oni 6

感谢Punit S的回答,为了检测动态连接速度变化,您可以使用以下代码:

navigator.connection.onchange = function () {
 //do what you need to do ,on speed change event
 console.log('Connection Speed Changed');
}
Run Code Online (Sandbox Code Playgroud)

  • 不幸的是它不支持所有浏览器。https://caniuse.com/#search=netinfo (10认同)

joh*_*ith 6

即使这是旧的并且得到了回答,我还是想分享我在 2020 年提出的解决方案

它具有随时运行的灵活性,并在大于或小于指定的 mbps 时运行回调

包含 testConnectionSpeed 对象后,您可以通过运行testConnectionSpeed.run(mbps, morefunction, lessfunction)

例如:

var testConnectionSpeed = {
  imageAddr : "https://upload.wikimedia.org/wikipedia/commons/a/a6/Brandenburger_Tor_abends.jpg", // this is just an example, you rather want an image hosted on your server
  downloadSize : 2707459, // this must match with the image above
  run:function(mbps_max,cb_gt,cb_lt){
    testConnectionSpeed.mbps_max = parseFloat(mbps_max) ? parseFloat(mbps_max) : 0;
    testConnectionSpeed.cb_gt = cb_gt;
    testConnectionSpeed.cb_lt = cb_lt;
    testConnectionSpeed.InitiateSpeedDetection();
  },
  InitiateSpeedDetection: function() {
    window.setTimeout(testConnectionSpeed.MeasureConnectionSpeed, 1);
  },
  result:function(){
    var duration = (endTime - startTime) / 1000;
    var bitsLoaded = testConnectionSpeed.downloadSize * 8;
    var speedBps = (bitsLoaded / duration).toFixed(2);
    var speedKbps = (speedBps / 1024).toFixed(2);
    var speedMbps = (speedKbps / 1024).toFixed(2);
    if(speedMbps >= (testConnectionSpeed.max_mbps ? testConnectionSpeed.max_mbps : 1) ){
      testConnectionSpeed.cb_gt ? testConnectionSpeed.cb_gt(speedMbps) : false;
    }else {
      testConnectionSpeed.cb_lt ? testConnectionSpeed.cb_lt(speedMbps) : false;
    }
  },
  MeasureConnectionSpeed:function() {
    var download = new Image();
    download.onload = function () {
        endTime = (new Date()).getTime();
        testConnectionSpeed.result();
    }
    startTime = (new Date()).getTime();
    var cacheBuster = "?nnn=" + startTime;
    download.src = testConnectionSpeed.imageAddr + cacheBuster;
  }
}




// start test immediatly, you could also call this on any event or whenever you want
testConnectionSpeed.run(1.5, function(mbps){console.log(">= 1.5Mbps ("+mbps+"Mbps)")}, function(mbps){console.log("< 1.5Mbps("+mbps+"Mbps)")} )
Run Code Online (Sandbox Code Playgroud)

我成功地使用它来加载低分辨率媒体以实现慢速互联网连接。您必须稍微尝试一下,因为一方面,图像越大,测试越合理,另一方面,慢速连接的测试将花费更长的时间,在我的情况下,我特别不希望连接速度慢的用户加载大量 MB。

  • 有什么办法可以提高上传速度吗? (2认同)

小智 5

最好使用图像来测试速度.但是如果你必须处理zip文件,下面的代码可以工作.

var fileURL = "your/url/here/testfile.zip";

var request = new XMLHttpRequest();
var avoidCache = "?avoidcache=" + (new Date()).getTime();;
request.open('GET', fileURL + avoidCache, true);
request.responseType = "application/zip";
var startTime = (new Date()).getTime();
var endTime = startTime;
request.onreadystatechange = function () {
    if (request.readyState == 2)
    {
        //ready state 2 is when the request is sent
        startTime = (new Date().getTime());
    }
    if (request.readyState == 4)
    {
        endTime = (new Date()).getTime();
        var downloadSize = request.responseText.length;
        var time = (endTime - startTime) / 1000;
        var sizeInBits = downloadSize * 8;
        var speed = ((sizeInBits / time) / (1024 * 1024)).toFixed(2);
        console.log(downloadSize, time, speed);
    }
}

request.send();
Run Code Online (Sandbox Code Playgroud)

文件<10MB时,这不会很好.您必须在多次下载尝试时运行聚合结果.

  • 我非常喜欢答案的简单性,我已根据自己的目的调整了它:我换了_window.performance.now_作为时间戳,_request.responseType ="blob"_(MIME类型无效),_ request.response.size_对于下载大小,和_1000000_用于速度计算(因为Mbps应该是SI单位). (3认同)

Ant*_*ony 5

改进约翰·史密斯的答案,一个漂亮而干净的解决方案,它返回一个 Promise,因此可以与async/await. 返回一个以 Mbps 为单位的值。

const imageAddr = 'https://upload.wikimedia.org/wikipedia/commons/a/a6/Brandenburger_Tor_abends.jpg';
const downloadSize = 2707459; // this must match with the image above

let startTime, endTime;
async function measureConnectionSpeed() {
  startTime = (new Date()).getTime();
  const cacheBuster = '?nnn=' + startTime;

  const download = new Image();
  download.src = imageAddr + cacheBuster;
  // this returns when the image is finished downloading
  await download.decode();
  endTime = (new Date()).getTime();
  const duration = (endTime - startTime) / 1000;
  const bitsLoaded = downloadSize * 8;
  const speedBps = (bitsLoaded / duration).toFixed(2);
  const speedKbps = (speedBps / 1024).toFixed(2);
  const speedMbps = (speedKbps / 1024).toFixed(2);
  return Math.round(Number(speedMbps));
}
Run Code Online (Sandbox Code Playgroud)