Como detectar a velocidade da internet em JavaScript?

213

Como posso criar uma página JavaScript que detecta a velocidade da Internet do usuário e a mostra na página? Algo como "a velocidade da sua internet é ?? / ?? Kb / s ” .

Sharon Haim Pour
fonte
1
@Jakub, @Ankit: As pessoas podem usar o Flash para isso, mas você não precisa . Não há razão alguma para que você não possa fazê-lo com JavaScript.
TJ Crowder
Isto é o que você precisa: speedof.me/api.html
advncd

Respostas:

287

É possível, até certo ponto, mas não será realmente preciso, a ideia é carregar a imagem com um tamanho de arquivo conhecido e, em seu onloadevento, medir quanto tempo passou até que o evento foi acionado e dividir esse tempo no tamanho do arquivo de imagem.

Exemplo pode ser encontrado aqui: Calcular velocidade usando javascript

Caso de teste que aplica a correção sugerida aqui:

//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>

A comparação rápida com o serviço de teste de velocidade "real" mostrou uma pequena diferença de 0,12 Mbps ao usar imagem grande.

Para garantir a integridade do teste, você pode executar o código com a otimização da ferramenta de desenvolvimento do Chrome ativada e ver se o resultado corresponde à limitação. (o crédito vai para user284130 :))

Coisas importantes a ter em mente:

  1. A imagem que está sendo usada deve ser adequadamente otimizada e compactada. Caso contrário, a compactação padrão nas conexões do servidor da web poderá mostrar uma velocidade maior do que realmente é. Outra opção é usar um formato de arquivo não compactável, por exemplo, jpg. (obrigado Rauli Rajande por apontar isso e Fluxine por me lembrar )

  2. O mecanismo de buster de cache descrito acima pode não funcionar com alguns servidores CDN, que podem ser configurados para ignorar os parâmetros da string de consulta, portanto, definindo melhor os cabeçalhos de controle de cache na própria imagem. (obrigado orcaman por apontar isso ) )

Shadow Wizard é Ear For You
fonte
8
Certifique-se de que a imagem de teste seja adequadamente otimizada e compactada. Caso contrário, a compactação padrão nas conexões por servidor da web pode mostrar uma velocidade maior do que realmente é.
Rauli Rajande
3
Encontrei um pequeno truque para garantir que sua imagem seja adequada para o teste: execute o código com a otimização da ferramenta de desenvolvimento do Chrome ativada e verifique se o resultado corresponde à limitação. Espero que isso possa ajudar alguém.
user284130
3
ingressar no Rauli Rajande: use melhor um arquivo não compactável (ou quase), ou os módulos de compactação do servidor da web podem reduzi-lo significativamente, invalidando a medida. Uma imagem JPEG seria uma boa escolha.
Fluxine
1
Para aqueles que usaram esse código Javascript com sucesso, você inicialmente não encontrou nenhuma chamada para "download.onload"? É exatamente isso que estou experimentando e ainda estou tentando descobrir o porquê.
2
A imagem menor do @Dilip significa teste menos preciso, é grande de propósito. :)
Shadow Wizard é Ear For You
78

Bem, como estamos em 2017, agora você tem a API de informações de rede (embora com um suporte limitado entre navegadores a partir de agora) para obter algum tipo de estimativa de informações de velocidade de downlink:

navigator.connection.downlink

Essa é uma estimativa de largura de banda efetiva em Mbits por segundo. O navegador faz essa estimativa a partir da taxa de transferência da camada de aplicativo observada recentemente em conexões ativas recentemente. Desnecessário dizer que a maior vantagem dessa abordagem é que você não precisa baixar nenhum conteúdo apenas para o cálculo da largura de banda / velocidade.

Você pode ver isso e outros atributos relacionados aqui

Devido ao seu suporte limitado e diferentes implementações nos navegadores (a partir de novembro de 2017), é altamente recomendável ler isso em detalhes

Punit S
fonte
18
Isso é muito vermelho em Posso usar!
Francisco Presencia 30/06
2
Não consigo números maiores que 10 MBit usando isso. Existe um limite?
Tobi
@Tobi Também não pareço ter mais de 10 MBit, deve ser mais como
100 MBit
O que exatamente é o downlink? É a velocidade de download ou algo assim?
gacat
@Tobi Nem eu, se a velocidade for superior a 10Mb, continuo lendo 10
Aramil
21

Como descrevi nesta outra resposta aqui no StackOverflow , você pode fazer isso cronometrando o download de arquivos de vários tamanhos (comece pequeno, aumente se a conexão parece permitir), garantindo através de cabeçalhos de cache e de forma que o arquivo seja realmente sendo lido do servidor remoto e não sendo recuperado do cache. Isso não requer necessariamente que você tenha um servidor próprio (os arquivos podem ser provenientes do S3 ou similar), mas você precisará de um local para obter os arquivos, a fim de testar a velocidade da conexão.

Dito isso, os testes de largura de banda point-in-time são notoriamente não confiáveis, pois são impactados por outros itens sendo baixados em outras janelas, a velocidade do seu servidor, links no caminho etc. etc. Mas você pode ter uma idéia aproximada usando esse tipo de técnica.

TJ Crowder
fonte
1
@ Jakub: Você teria que ter um local para fazer o upload, mas não há razão para não usar a mesma técnica para isso. Você pode usar os dados gerados on-the-fly ou, é claro, reutilizar alguns dos dados baixados para o teste de download.
TJ Crowder
Então, como você saberia quando o upload foi concluído?
precisa saber é o seguinte
2
@ Jakub: Qualquer uma das várias maneiras. Se você enviar um formulário para um oculto iframe, por exemplo, você pesquisa o iframeou um cookie para conclusão. Se você usar um XMLHttpRequestobjeto para fazer a postagem, haverá um retorno de chamada para conclusão.
TJ Crowder
17

Eu precisava de uma maneira rápida de determinar se a velocidade de conexão do usuário era rápida o suficiente para ativar / desativar alguns recursos em um site em que estou trabalhando. Criei este pequeno script que calcula a média do tempo necessário para baixar uma única imagem (pequena) várias vezes, está funcionando com muita precisão nos meus testes, sendo capaz de distinguir claramente entre 3G ou Wi-Fi, por exemplo, talvez alguém possa criar uma versão mais elegante ou até mesmo um plugin 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);
  }
}

dmm79
fonte
1
A resposta mais confiável, no meu caso.
Abdalla Arbab
1
e o teste de upload?
gumuruh 22/03
9

O truque da imagem é legal, mas nos meus testes ele estava carregando antes de algumas chamadas do ajax que eu queria concluir.

A solução adequada em 2017 é usar um trabalhador ( http://caniuse.com/#feat=webworkers ).

O trabalhador terá a seguinte aparência:

/**
 * 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
  );
};

O arquivo js que chamará o Worker:

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);
  }
};

Código retirado de um pacote Plone que escrevi:

alepisa
fonte
5

É melhor usar imagens para testar a velocidade. Mas se você precisar lidar com arquivos zip, o código abaixo funcionará.

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();

Isso não funcionará muito bem com arquivos <10 MB. Você precisará executar resultados agregados em várias tentativas de download.

Akshar
fonte
3
Eu realmente gosto da simplicidade da resposta e a adaptei para minha finalidade: troquei para window.performance.now pelos carimbos de data / hora, request.responseType = "blob" (os tipos MIME não são válidos), request.response.size for o tamanho do download e 1000000 para o cálculo da velocidade (porque o Mbps deve estar em unidades SI).
Rupert Rawnsley
1

Graças à resposta Punit S, para detectar alterações dinâmicas na velocidade da conexão, você pode usar o seguinte código:

navigator.connection.onchange = function () {
 //do what you need to do ,on speed change event
 console.log('Connection Speed Changed');
}
Mehdi Maghrooni
fonte
2
infelizmente, ele não suporta todos os navegadores. caniuse.com/#search=netinfo
axelioo