Ricevi l'URL dei dati delle immagini in JavaScript?


335

Ho una normale pagina HTML con alcune immagini (solo normali <img />tag HTML). Vorrei ottenere il loro contenuto, codificato in base64 preferibilmente, senza la necessità di scaricare nuovamente l'immagine (cioè è già caricata dal browser, quindi ora voglio il contenuto).

Mi piacerebbe raggiungerlo con Greasemonkey e Firefox.

Risposte:


400

Nota: funziona solo se l'immagine proviene dallo stesso dominio della pagina o ha l' crossOrigin="anonymous"attributo e il server supporta CORS. Inoltre non ti fornirà il file originale, ma una versione ricodificata. Se hai bisogno che il risultato sia identico all'originale, vedi la risposta di Kaiido .


Sarà necessario creare un elemento canvas con le dimensioni corrette e copiare i dati dell'immagine con la drawImagefunzione. Quindi puoi usare la toDataURLfunzione per ottenere un dato: url che ha l'immagine codificata in base 64. Si noti che l'immagine deve essere completamente caricata, oppure si torna indietro un'immagine vuota (nera, trasparente).

Sarebbe qualcosa del genere. Non ho mai scritto uno script Greasemonkey, quindi potrebbe essere necessario modificare il codice per l'esecuzione in quell'ambiente.

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

Ottenere un'immagine in formato JPEG non funziona con le versioni precedenti (circa 3.5) di Firefox, quindi se vuoi supportarlo, devi verificarne la compatibilità. Se la codifica non è supportata, verrà automaticamente impostata su "image / png".


1
Mentre questo sembra funzionare (tranne il non convertito / nel ritorno), non crea la stessa stringa base64 di quella che sto ricevendo da PHP quando eseguo base64_encode sul file ottenuto con la funzione file_get_contents. Le immagini sembrano molto simili / uguali, tuttavia quella Javascripted è più piccola e mi piacerebbe che fossero esattamente le stesse. Ancora una cosa: l'immagine di input è un piccolo PNG (594 byte), 28x30 con sfondo trasparente, se cambia qualcosa.
Detariael,

7
Firefox potrebbe utilizzare un livello di compressione diverso che influirebbe sulla codifica. Inoltre, penso che PNG supporti alcune informazioni di intestazione extra come le note, che andrebbero perse, poiché la tela riceve solo i dati pixel. Se hai bisogno che sia esattamente lo stesso, potresti probabilmente usare AJAX per ottenere il file e base64 codificarlo manualmente.
Matthew Crumley,

7
Sì, scaricare l'immagine con XMLHttpRequest. Si spera che utilizzerebbe la versione cache dell'immagine, ma ciò dipenderebbe dalla configurazione del server e del browser e si avrebbe l'overhead della richiesta per determinare se il file è cambiato. Ecco perché non ho suggerito che in primo luogo :-) Purtroppo, per quanto ne so, è l'unico modo per ottenere il file originale.
Matthew Crumley,

2
@trusktr Non drawImagefarà nulla e finirai con una tela bianca e l'immagine risultante.
Matthew Crumley,

1
@JohnSewell Questo è solo perché l'OP voleva il contenuto, non un URL. Dovresti saltare la chiamata di sostituzione (...) se vuoi usarla come fonte di immagini.
Matthew Crumley,

76

Questa funzione accetta l'URL, quindi restituisce l'immagine BASE64

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

Chiamalo così: getBase64FromImageUrl("images/slbltxt.png")


8
c'è un modo per trasformare un'immagine da un collegamento esterno alla base 64?
dinodsaurus

@dinodsaurus potresti caricarlo in un tag immagine o fare una query ajax.
Jared Forsyth,

6
Bene, richiede loro di implementare CORS, ma poi fai solo $ .ajax (theimageurl) e dovrebbe restituire qualcosa di ragionevole. Altrimenti (se non hanno CORS abilitato) non funzionerà; il modello di sicurezza di Internet non lo consente. A meno che, ovviamente, non ci si trovi all'interno di un plug-in di Chrome, nel qual caso tutto è permesso - anche nell'esempio sopra
Jared Forsyth

4
Devi metterlo img.src =dopo img.onload =, perché in alcuni browser, come Opera, l'evento non accadrà.
ostapische,

1
@Hakkar si verificherà una perdita di memoria solo nei vecchi browser che utilizzano il conteggio dei riferimenti continui per informare la garbage collection. Per la mia comprensione, il riferimento circolare non crea perdite in un segno e una scansione . Una volta che gli scopi delle funzioni di getBase64FromImageUrl(url)e img.onload = function ()sono usciti, img è irraggiungibile e la raccolta dei rifiuti. Altro che IE 6/7 e questo è ok.
umanità e

59

Prossimamente, ma nessuna delle risposte qui è del tutto corretta.

Quando disegnato su una tela, l'immagine passata è non compressa + tutto pre-moltiplicato.
Quando viene esportato, viene decompresso o ricompresso con un algoritmo diverso e non moltiplicato.

Tutti i browser e i dispositivi presenteranno errori di arrotondamento diversi durante questo processo
(consultare Impronta digitale su tela ).

Quindi, se si desidera una versione base64 di un file di immagine, è necessario richiederlo di nuovo (il più delle volte verrà dalla cache) ma questa volta come BLOB.

Quindi è possibile utilizzare un FileReader per leggerlo come ArrayBuffer o come dataURL.

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


Ho appena superato 5+ domande diverse e il tuo codice è l'unico che ha funzionato correttamente, grazie :)
Peter

1
Ottengo questo errore con il codice sopra. XMLHttpRequest cannot load data:image/jpeg;base64,/9j/4AA ... /2Q==. Invalid response. Origin 'https://example.com' is therefore not allowed access.
STWilson,

2
@STWilson, sì, anche questo metodo è legato alla stessa politica di origine, proprio come quella di tela. In caso di richiesta di origine incrociata, è necessario configurare il server di hosting per consentire tali richieste di origine incrociata allo script.
Kaiido,

@Kaiido quindi se l'immagine si trova su un server diverso dallo script il server di hosting deve abilitare le richieste di origine incrociata? Se imposti img.setAttribute ('crossOrigin', 'anonymous') questo impedisce l'errore?
1,21 gigawatt

1
Dopo ulteriori ricerche, sembra che l'immagine possa utilizzare l'attributo crossOrigin per richiedere l'accesso, ma spetta al server di hosting fornire l'accesso tramite un'intestazione CORS, sitepoint.com/an-in-depth-look-at-cors . Per quanto riguarda XMLHttpRequest, sembra che il server debba dare lo stesso accesso alle immagini tramite un'intestazione CORS, ma non è richiesta alcuna modifica nel codice tranne forse l'impostazione di xhr.withCredentials su false (impostazione predefinita).
1,21 gigawatt

20

Una versione più moderna della risposta di kaiido usando fetch sarebbe:

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

Modifica: Come sottolineato nei commenti, questo restituirà un url oggetto che punta a un file nel tuo sistema locale anziché a un DataURL reale, quindi a seconda del tuo caso d'uso questo potrebbe non essere quello che ti serve.

Puoi guardare la seguente risposta per usare fetch e un dato realeURL: https://stackoverflow.com/a/50463054/599602


2
Non dimenticare di chiamare URL.revokeObjectURL()se si dispone di un'applicazione a esecuzione prolungata che utilizza questo metodo o se la memoria si ingombra.
Ingegnere

1
Attenzione: DataURL (codificato Base64) non è lo stesso di ObjectURL (riferimento al
BLOB

1
ObjectURL non è certamente un URL di dati. Questa non è una risposta corretta
Danny Lin l'

2

shiv / shim / sham

Se le tue immagini sono già state caricate (o meno), questo "strumento" potrebbe tornare utile:

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

.. ma perché?

Ciò ha il vantaggio di utilizzare i dati di immagine "già caricati", quindi non è necessaria alcuna richiesta aggiuntiva. Aditionally permette l'utente finale (programmatore come te) decidere le CORS e / o mime-typee qualityOPPURE si può lasciare fuori questi argomenti / parametri come descritto nella MDN specifiche qui .

Se hai caricato questo JS (prima di quando è necessario), la conversione in dataURLè semplice come:

esempi

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

Fingerprinting GPU

Se sei preoccupato per la "precisione" dei bit, puoi modificare questo strumento in base alle tue esigenze, come fornito dalla risposta di @ Kaiido.


1

Utilizzare l' onloadevento per convertire l'immagine dopo il caricamento


-3

In HTML5 meglio usare questo:

{
//...
canvas.width = img.naturalWidth; //img.width;
canvas.height = img.naturalHeight; //img.height;
//...
}

2
Mentre rispondi alla domanda, cerca di essere più specifico e fornisci spiegazioni dettagliate.
Piyush Zalani,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.