Jak wykryć prędkość internetu w JavaScript?


213

Jak mogę utworzyć stronę JavaScript, która wykryje prędkość Internetu użytkownika i wyświetli go na stronie? Coś w stylu „Twoja prędkość Internetu wynosi ?? / ?? Kb / s ” .


1
@Jakub, @Ankit: ludzie mogą do tego używać Flasha, ale nie musisz . Bez powodu nie możesz tego zrobić za pomocą JavaScript.
TJ Crowder

Oto, czego potrzebujesz: speedof.me/api.html
advncd

Odpowiedzi:


287

Jest to w pewnym stopniu możliwe, ale nie będzie naprawdę dokładne, pomysł polega na załadowaniu obrazu o znanym rozmiarze pliku, a następnie w jego onloadzdarzeniu zmierz, ile czasu upłynęło do wyzwolenia tego zdarzenia, i podziel ten czas na rozmiar pliku obrazu.

Przykład można znaleźć tutaj: Oblicz prędkość za pomocą javascript

Przypadek testowy z zastosowaniem sugerowanej tam poprawki:

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

Szybkie porównanie z „rzeczywistą” usługą testowania prędkości wykazało niewielką różnicę 0,12 Mb / s przy użyciu dużego obrazu.

Aby zapewnić integralność testu, możesz uruchomić kod z włączonym ograniczeniem narzędzia deweloperskiego Chrome, a następnie sprawdzić, czy wynik odpowiada ograniczeniu. (kredyt trafia do użytkownika 284130 :))

Ważne rzeczy, o których należy pamiętać:

  1. Używany obraz powinien być odpowiednio zoptymalizowany i skompresowany. Jeśli tak nie jest, wówczas domyślna kompresja połączeń przez serwer WWW może wykazywać większą prędkość niż jest w rzeczywistości. Inną opcją jest użycie nieskompresowanego formatu pliku, np. Jpg. (dzięki Rauli Rajande za zwrócenie na to uwagi i Fluxine za przypomnienie )

  2. Opisany powyżej mechanizm pomijania pamięci podręcznej może nie działać z niektórymi serwerami CDN, które można skonfigurować tak, aby ignorowały parametry ciągu zapytania, dlatego lepiej ustawić nagłówki kontroli pamięci podręcznej na samym obrazie. (dzięki orcaman za zwrócenie na to uwagi ) )


8
Uważaj, aby obraz testowy był odpowiednio zoptymalizowany i skompresowany. Jeśli tak nie jest, wówczas domyślna kompresja połączeń przez serwer WWW może pokazywać większą prędkość niż jest w rzeczywistości.
Rauli Rajande,

3
Znalazłem małą sztuczkę, aby upewnić się, że Twój obraz jest odpowiedni do testu: uruchom kod z włączonym ograniczeniem narzędzia deweloperskiego Chrome i sprawdź, czy wynik pasuje do ograniczenia. Mam nadzieję, że to może komuś pomóc.
user284130,

3
dołączenie do Rauli Rajande: lepiej użyj pliku, który jest nieskompresowalny (lub prawie), ponieważ moduły kompresji serwera WWW mogą go znacznie zmniejszyć, unieważniając miarę. Dobrym wyborem byłby obraz JPEG.
Fluxine

1
Czy w przypadku osób, które z powodzeniem wykorzystały ten kod JavaScript, na początku nie było żadnych wywołań dotyczących „download.onload”? Właśnie tego doświadczam i wciąż próbuję dowiedzieć się, dlaczego.

2
@Dilip mniejszy obraz oznacza mniej dokładny test, jest duży celowo. :)
Shadow Wizard is Ear For You

78

Cóż, to jest 2017 rok, więc masz teraz Network Information API (choć z ograniczoną obsługą w różnych przeglądarkach), aby uzyskać szacunkowe informacje o prędkości łącza w dół:

navigator.connection.downlink

Jest to efektywne oszacowanie przepustowości w Mb / s na sekundę. Przeglądarka dokonuje tego oszacowania na podstawie ostatnio zaobserwowanej przepustowości warstwy aplikacji dla ostatnio aktywnych połączeń. Nie trzeba dodawać, że największą zaletą tego podejścia jest to, że nie trzeba pobierać żadnych treści tylko w celu obliczenia przepustowości / prędkości.

Możesz zobaczyć to i kilka innych powiązanych atrybutów tutaj

Ze względu na ograniczoną obsługę i różne implementacje w różnych przeglądarkach (od listopada 2017 r.) Zdecydowanie zalecamy szczegółowe przeczytanie tego


18
To dużo czerwieni w Can I Use!
Francisco Presencia

2
Nie dostaję przy tym liczb wyższych niż 10 MBit. Czy jest jakiś limit?
Tobi

@Tobi Też nie wydaje mi się, aby uzyskać więcej niż 10 MBit, powinien być bardziej jak 100 MBit
camjocotem

Czym dokładnie jest łącze w dół? Czy to prędkość pobierania czy coś takiego?
gacat

@Tobi Ja też, jeśli prędkość przekracza 10 Mb, wciąż czytam 10
Aramil

21

Jak zarysowuję w tej innej odpowiedzi tutaj na StackOverflow , możesz to zrobić, odmierzając czas pobierania plików o różnych rozmiarach (zacznij od małego, zwiększ, jeśli połączenie wydaje się na to pozwalać), zapewniając przez nagłówki pamięci podręcznej i takie, że plik jest naprawdę czyta się ze zdalnego serwera i nie jest pobierana z pamięci podręcznej. Niekoniecznie wymaga to posiadania własnego serwera (pliki mogą pochodzić z S3 lub podobnego), ale będziesz potrzebować skądś, aby pobrać pliki, aby przetestować szybkość połączenia.

To powiedziawszy, testy przepustowości punktu w czasie są notorycznie niewiarygodne, ponieważ mają na nie wpływ inne elementy pobierane w innych oknach, prędkość serwera, łącza na trasie itp. Itp. Ale możesz mieć ogólny pomysł używając tego rodzaju techniki.


1
@Jakub: Musisz mieć miejsce do przesyłania, ale nie ma powodu, dla którego nie możesz użyć tej samej techniki. Możesz użyć danych, które generujesz w locie lub, oczywiście, możesz ponownie użyć niektórych danych pobranych do testu pobierania.
TJ Crowder

Skąd więc wiesz, kiedy przesyłanie zostało ukończone?
Jakub Hampl

2
@Jakub: Dowolny z kilku sposobów. Jeśli iframena przykład przesyłasz formularz do ukrytego , odpytujesz iframeplik cookie lub plik cookie w celu uzupełnienia. Jeśli użyjesz XMLHttpRequestobiektu do wykonania posta, nastąpi oddzwonienie do ukończenia.
TJ Crowder

17

Potrzebowałem szybkiego sposobu, aby ustalić, czy szybkość połączenia użytkownika jest wystarczająco duża, aby włączyć / wyłączyć niektóre funkcje w witrynie, nad którą pracuję. Stworzyłem ten mały skrypt, który uśrednia czas potrzebny do pobrania pojedynczego (małego) obrazu wiele razy działa całkiem dokładnie w moich testach, będąc w stanie wyraźnie odróżnić na przykład 3G lub Wi-Fi, może ktoś może stworzyć bardziej elegancką wersję, a nawet wtyczkę 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);
  }
}


1
Najbardziej wiarygodna odpowiedź w moim przypadku.
Abdalla Arbab

1
co z testem przesyłania?
gumuruh

9

Sztuczka z obrazem jest fajna, ale w moich testach ładowała się przed niektórymi wywołaniami ajax, które chciałem zakończyć.

Właściwym rozwiązaniem w 2017 roku jest skorzystanie z pracownika ( http://caniuse.com/#feat=webworkers ).

Pracownik będzie wyglądał następująco:

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

Plik js, który wywoła proces roboczy:

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

Kod pobrany z pakietu Plone, który napisałem:


5

Lepiej jest używać obrazów do testowania prędkości. Ale jeśli masz do czynienia z plikami zip, poniższy kod działa.

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

To nie będzie działać dobrze z plikami <10 MB. Będziesz musiał uruchomić zagregowane wyniki przy wielu próbach pobierania.


3
Bardzo podoba mi się prostota odpowiedzi i dostosowałem ją do moich celów: zamieniłem na window.performance.now dla znaczników czasu, request.responseType = "blob" (typy MIME nie są poprawne), request.response.size dla rozmiar pobierania i 1000000 do obliczenia prędkości (ponieważ Mbps powinny być w jednostkach SI).
Rupert Rawnsley,


1

dzięki odpowiedzi Punit S, w celu wykrycia dynamicznej zmiany prędkości połączenia, możesz użyć następującego kodu:

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

2
niestety nie obsługuje wszystkich przeglądarek. caniuse.com/#search=netinfo
axelioo
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.