Bilddaten-URL in JavaScript abrufen?

335

Ich habe eine normale HTML-Seite mit einigen Bildern (nur normale <img />HTML-Tags). Ich möchte ihren Inhalt, vorzugsweise Base64-codiert, erhalten, ohne dass das Bild erneut heruntergeladen werden muss (dh es wird bereits vom Browser geladen, daher möchte ich jetzt den Inhalt).

Ich würde das gerne mit Greasemonkey und Firefox erreichen.

Detariael
quelle

Antworten:

400

Hinweis: Dies funktioniert nur, wenn das Image aus derselben Domäne wie die Seite stammt oder das crossOrigin="anonymous"Attribut hat und der Server CORS unterstützt. Sie erhalten auch nicht die Originaldatei, sondern eine neu codierte Version. Wenn das Ergebnis mit dem Original identisch sein soll, lesen Sie die Antwort von Kaiido .


Sie müssen ein Canvas-Element mit den richtigen Abmessungen erstellen und die Bilddaten mit der drawImageFunktion kopieren . Dann können Sie die toDataURLFunktion verwenden, um eine Daten-URL abzurufen, die das Base-64-codierte Bild enthält. Beachten Sie, dass das Bild vollständig geladen sein muss, sonst erhalten Sie nur ein leeres (schwarzes, transparentes) Bild zurück.

Es wäre so etwas. Ich habe noch nie ein Greasemonkey-Skript geschrieben, daher müssen Sie möglicherweise den Code anpassen, um in dieser Umgebung ausgeführt zu werden.

function getBase64Image(img) {
    // Create an empty canvas element
    var canvas = document.createElement("canvas");
    canvas.width = img.width;
    canvas.height = img.height;

    // Copy the image contents to the canvas
    var ctx = canvas.getContext("2d");
    ctx.drawImage(img, 0, 0);

    // Get the data-URL formatted image
    // Firefox supports PNG and JPEG. You could check img.src to
    // guess the original format, but be aware the using "image/jpg"
    // will re-encode the image.
    var dataURL = canvas.toDataURL("image/png");

    return dataURL.replace(/^data:image\/(png|jpg);base64,/, "");
}

Das Abrufen eines JPEG-formatierten Bildes funktioniert in älteren Versionen (ca. 3.5) von Firefox nicht. Wenn Sie dies unterstützen möchten, müssen Sie die Kompatibilität überprüfen. Wenn die Codierung nicht unterstützt wird, wird standardmäßig "image / png" verwendet.

Matthew Crumley
quelle
1
Während dies zu funktionieren scheint (mit Ausnahme des nicht entkappten / in der Rückgabe), wird nicht derselbe base64-String erstellt, den ich von PHP erhalte, wenn base64_encode für die mit der Funktion file_get_contents erhaltene Datei ausgeführt wird. Die Bilder scheinen sehr ähnlich / gleich zu sein, dennoch ist das Javascripted kleiner und ich würde es lieben, wenn sie genau gleich wären. Noch etwas: Das Eingabebild ist ein kleines (594 Byte) 28x30 PNG mit transparentem Hintergrund - wenn sich dadurch etwas ändert.
Detariael
7
Firefox verwendet möglicherweise eine andere Komprimierungsstufe, die sich auf die Codierung auswirkt. Ich denke auch, dass PNG einige zusätzliche Header-Informationen wie Notizen unterstützt, die verloren gehen würden, da die Leinwand nur die Pixeldaten erhält. Wenn es genau gleich sein muss, können Sie wahrscheinlich AJAX verwenden, um die Datei abzurufen und Base64 manuell zu codieren.
Matthew Crumley
7
Ja, Sie würden das Bild mit XMLHttpRequest herunterladen. Hoffentlich wird die zwischengespeicherte Version des Bildes verwendet, dies hängt jedoch von der Server- und Browserkonfiguration ab, und Sie haben den Anforderungsaufwand, um festzustellen, ob sich die Datei geändert hat. Deshalb habe ich das überhaupt nicht vorgeschlagen :-) Leider ist es meines Wissens die einzige Möglichkeit, die Originaldatei zu erhalten.
Matthew Crumley
2
@trusktr Der drawImagewird nichts tun, und Sie erhalten eine leere Leinwand und das resultierende Bild.
Matthew Crumley
1
@ JohnSewell Das liegt nur daran, dass das OP den Inhalt wollte, keine URL. Sie müssten den Aufruf zum Ersetzen (...) überspringen, wenn Sie ihn als Bildquelle verwenden möchten.
Matthew Crumley
76

Diese Funktion verwendet die URL und gibt dann das Bild BASE64 zurück

function getBase64FromImageUrl(url) {
    var img = new Image();

    img.setAttribute('crossOrigin', 'anonymous');

    img.onload = function () {
        var canvas = document.createElement("canvas");
        canvas.width =this.width;
        canvas.height =this.height;

        var ctx = canvas.getContext("2d");
        ctx.drawImage(this, 0, 0);

        var dataURL = canvas.toDataURL("image/png");

        alert(dataURL.replace(/^data:image\/(png|jpg);base64,/, ""));
    };

    img.src = url;
}

Nennen Sie es so: getBase64FromImageUrl("images/slbltxt.png")

MuniR
quelle
8
Gibt es eine Möglichkeit, ein Bild von einem externen Link zur Basis 64 zu wechseln?
Dinodsaurus
@dinodsaurus Sie können es in ein Bild-Tag laden oder eine Ajax-Abfrage durchführen.
Jared Forsyth
6
Nun, es erfordert, dass sie CORS implementieren, aber dann machen Sie einfach $ .ajax (theimageurl) und es sollte etwas Vernünftiges zurückgeben. Andernfalls (wenn CORS nicht aktiviert ist) funktioniert es nicht. Das Sicherheitsmodell des Internets verbietet es. Es sei denn natürlich, Sie befinden sich in einem Chrome-Plugin. In diesem Fall ist alles erlaubt - auch das obige Beispiel
Jared Forsyth
4
Sie müssen img.src =nach setzen img.onload =, da in einigen Browsern, wie z. B. Opera, das Ereignis nicht auftritt.
ostapische
1
@Hakkar Ein Speicherverlust tritt nur in alten Browsern auf, die noch die Referenzanzahl verwenden , um die Speicherbereinigung zu informieren. Nach meinem Verständnis verursacht der Zirkelverweis kein Leck in einem Markierungs- und Sweep- Setup. Sobald die Funktionsbereiche von getBase64FromImageUrl(url)und beendet img.onload = function ()sind, ist img nicht mehr erreichbar und der Müll wird gesammelt. Anders als IE 6/7 und das ist in Ordnung.
Menschlichkeit und
59

Es kommt lange danach, aber keine der Antworten hier ist völlig richtig.

Beim Zeichnen auf einer Leinwand wird das übergebene Bild unkomprimiert + alle vormultipliziert.
Beim Export wird es unkomprimiert oder mit einem anderen Algorithmus erneut komprimiert und nicht multipliziert.

Bei allen Browsern und Geräten treten unterschiedliche Rundungsfehler auf
(siehe Canvas-Fingerabdruck) ).

Wenn man also eine base64-Version einer Bilddatei möchte, muss man sie erneut anfordern (meistens kommt sie aus dem Cache), diesmal jedoch als Blob.

Anschließend können Sie einen FileReader verwenden , um ihn entweder als ArrayBuffer oder als dataURL zu lesen.

function toDataURL(url, callback){
    var xhr = new XMLHttpRequest();
    xhr.open('get', url);
    xhr.responseType = 'blob';
    xhr.onload = function(){
      var fr = new FileReader();
    
      fr.onload = function(){
        callback(this.result);
      };
    
      fr.readAsDataURL(xhr.response); // async call
    };
    
    xhr.send();
}

toDataURL(myImage.src, function(dataURL){
  result.src = dataURL;

  // now just to show that passing to a canvas doesn't hold the same results
  var canvas = document.createElement('canvas');
  canvas.width = myImage.naturalWidth;
  canvas.height = myImage.naturalHeight;
  canvas.getContext('2d').drawImage(myImage, 0,0);

  console.log(canvas.toDataURL() === dataURL); // false - not same data
  });
<img id="myImage" src="https://dl.dropboxusercontent.com/s/4e90e48s5vtmfbd/aaa.png" crossOrigin="anonymous">
<img id="result">

Kaiido
quelle
Ich habe gerade mehr als 5 verschiedene Fragen durchlaufen und dein Code ist der einzige, der richtig funktioniert hat, danke :)
Peter
1
Ich erhalte diesen Fehler mit dem obigen Code. XMLHttpRequest cannot load data:image/jpeg;base64,/9j/4AA ... /2Q==. Invalid response. Origin 'https://example.com' is therefore not allowed access.
STWilson
2
@STWilson, ja, diese Methode ist ebenso wie die Canvas-Methode an die Richtlinie mit demselben Ursprung gebunden. Im Falle einer Cross-Origin-Anfrage müssen Sie den Hosting-Server so konfigurieren, dass solche Cross-Origin-Anfragen an Ihr Skript zugelassen werden.
Kaiido
@Kaiido Wenn sich das Image auf einem anderen Server als dem Skript befindet, muss der Hosting-Server Ursprungsübergreifende Anforderungen aktivieren? Wenn Sie img.setAttribute ('crossOrigin', 'anonym') festlegen, verhindert dies den Fehler?
1,21 Gigawatt
1
Nach weiteren Recherchen sieht es so aus, als könnten Bilder das crossOrigin-Attribut verwenden, um den Zugriff anzufordern . Es ist jedoch Sache des Hosting-Servers, den Zugriff über einen CORS-Header ( sitepoint.com/an-in-depth-look-at-cors) zu gewähren . Bei XMLHttpRequest sieht es so aus, als müsste der Server den gleichen Zugriff wie für Bilder über einen CORS-Header gewähren, aber es ist keine Änderung in Ihrem Code erforderlich, außer möglicherweise xhr.withCredentials auf false zu setzen (Standardeinstellung).
1,21 Gigawatt
20

Eine modernere Version von Kaiidos Antwort mit Fetch wäre:

function toObjectUrl(url) {
  return fetch(url)
      .then((response)=> {
        return response.blob();
      })
      .then(blob=> {
        return URL.createObjectURL(blob);
      });
}

https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API/Using_Fetch

Bearbeiten: Wie in den Kommentaren erwähnt, gibt dies eine Objekt-URL zurück, die auf eine Datei in Ihrem lokalen System anstelle einer tatsächlichen DataURL verweist. Abhängig von Ihrem Anwendungsfall ist dies möglicherweise nicht das, was Sie benötigen.

Sie können sich die folgende Antwort ansehen, um Fetch und eine tatsächliche Daten-URL zu verwenden: https://stackoverflow.com/a/50463054/599602

Zaptree
quelle
2
Vergessen Sie nicht anzurufen, URL.revokeObjectURL()wenn Sie eine lang laufende Anwendung mit dieser Methode haben, sonst wird Ihr Speicher überfüllt.
Ingenieur
1
Achtung: DataURL (base64-codiert) ist nicht dasselbe wie ObjectURL (Verweis auf Blob)
DaniEll
1
ObjectURL ist sicherlich keine Daten-URL. Dies ist keine richtige Antwort.
Danny Lin
2

shiv / shim / sham

Wenn Ihre Bilder bereits geladen sind (oder nicht), kann dieses "Werkzeug" nützlich sein:

Object.defineProperty
(
    HTMLImageElement.prototype,'toDataURL',
    {enumerable:false,configurable:false,writable:false,value:function(m,q)
    {
        let c=document.createElement('canvas');
        c.width=this.naturalWidth; c.height=this.naturalHeight;
        c.getContext('2d').drawImage(this,0,0); return c.toDataURL(m,q);
    }}
);

.. aber warum?

Dies hat den Vorteil, dass die "bereits geladenen" Bilddaten verwendet werden, sodass keine zusätzliche Anforderung erforderlich ist. Zusätzlich kann der Endbenutzer (Programmierer wie Sie) das CORS entscheiden und / oder mime-typeund quality-OR- Sie können diese Argumente / Parameter weglassen, wie in der MDN- Spezifikation hier beschrieben .

Wenn Sie dieses JS geladen haben (bevor es benötigt wird), konvertieren Sie zu dataURL so einfach wie:

Beispiele

HTML
<img src="/yo.jpg" onload="console.log(this.toDataURL('image/jpeg'))">
JS
console.log(document.getElementById("someImgID").toDataURL());

GPU-Fingerabdruck

Wenn Sie sich Sorgen über die "Genauigkeit" der Bits machen, können Sie dieses Tool an Ihre Bedürfnisse anpassen, wie in der Antwort von @ Kaiido angegeben.

Argon
quelle
1

Verwenden Sie onloadevent, um das Bild nach dem Laden zu konvertieren

Kamil Kiełczewski
quelle
-3

Verwenden Sie in HTML5 dies besser:

{
//...
canvas.width = img.naturalWidth; //img.width;
canvas.height = img.naturalHeight; //img.height;
//...
}
KepHec
quelle
2
Versuchen Sie bei der Beantwortung der Frage, genauer zu sein und detaillierte Erklärungen abzugeben.
Piyush Zalani