Dapatkan url data gambar dalam JavaScript?

335

Saya memiliki halaman HTML biasa dengan beberapa gambar (hanya <img />tag HTML biasa ). Saya ingin mendapatkan konten mereka, lebih disukai encoding base64, tanpa perlu mengunduh ulang gambar (mis. Itu sudah dimuat oleh browser, jadi sekarang saya ingin konten).

Saya ingin mencapainya dengan Greasemonkey dan Firefox.

Detariael
sumber

Jawaban:

400

Catatan: Ini hanya berfungsi jika gambar berasal dari domain yang sama dengan halaman, atau memiliki crossOrigin="anonymous"atribut dan server mendukung CORS. Ini juga tidak akan memberi Anda file asli, tetapi versi yang disandikan ulang. Jika Anda ingin hasilnya identik dengan aslinya, lihat jawaban Kaiido .


Anda harus membuat elemen kanvas dengan dimensi yang benar dan menyalin data gambar dengan drawImagefungsinya. Kemudian Anda dapat menggunakan toDataURLfungsi untuk mendapatkan data: url yang memiliki gambar dasar-64 yang disandikan. Perhatikan bahwa gambar harus dimuat sepenuhnya, atau Anda hanya akan mendapatkan kembali gambar kosong (hitam, transparan).

Itu akan menjadi seperti ini. Saya tidak pernah menulis skrip Greasemonkey, jadi Anda mungkin perlu menyesuaikan kode untuk dijalankan di lingkungan itu.

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

Mendapatkan gambar berformat JPEG tidak berfungsi pada versi Firefox yang lebih lama (sekitar 3,5), jadi jika Anda ingin mendukungnya, Anda perlu memeriksa kompatibilitasnya. Jika pengkodean tidak didukung, itu akan default ke "image / png".

Matthew Crumley
sumber
1
Meskipun ini tampaknya berfungsi (kecuali unescaped / in the return), itu tidak membuat string base64 yang sama dengan yang saya dapatkan dari PHP ketika melakukan base64_encode pada file yang diperoleh dengan fungsi file_get_contents. Gambar tampak sangat mirip / sama, masih yang Javascript lebih kecil dan saya ingin mereka persis sama. Satu hal lagi: gambar input adalah kecil (594 byte), 28x30 PNG dengan latar belakang transparan - jika itu mengubah apa pun.
Detariael
7
Firefox dapat menggunakan level kompresi berbeda yang akan memengaruhi encoding. Juga, saya pikir PNG mendukung beberapa informasi tajuk tambahan seperti catatan, yang akan hilang, karena kanvas hanya mendapatkan data piksel. Jika Anda membutuhkannya persis sama, Anda mungkin bisa menggunakan AJAX untuk mendapatkan file dan base64 menyandikannya secara manual.
Matthew Crumley
7
Ya, Anda akan mengunduh gambar dengan XMLHttpRequest. Mudah-mudahan, itu akan menggunakan versi cache gambar, tetapi itu akan tergantung pada konfigurasi server dan browser, dan Anda akan memiliki overhead permintaan untuk menentukan apakah file telah berubah. Itu sebabnya saya tidak menyarankan itu sejak awal :-) Sayangnya, sejauh yang saya tahu, itu satu-satunya cara untuk mendapatkan file asli.
Matthew Crumley
2
@trusktr The drawImagetidak akan melakukan apa-apa, dan Anda akan berakhir dengan kanvas kosong dan gambar yang dihasilkan.
Matthew Crumley
1
@ JohnSewell Itu hanya karena OP menginginkan konten, bukan URL. Anda harus melewati panggilan ganti (...) jika Anda ingin menggunakannya sebagai sumber gambar.
Matthew Crumley
76

Fungsi ini mengambil URL lalu mengembalikan gambar 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;
}

Sebut saja seperti ini: getBase64FromImageUrl("images/slbltxt.png")

Munir
sumber
8
apakah ada cara untuk mengubah gambar dari tautan eksternal ke basis 64?
dinodsaurus
@dinodsaurus Anda dapat memuatnya dalam tag gambar, atau melakukan kueri ajax.
Jared Forsyth
6
Yah, itu mengharuskan mereka untuk menerapkan CORS, tetapi kemudian Anda hanya melakukan $ .ajax (theimageurl) dan itu harus mengembalikan sesuatu yang masuk akal. Kalau tidak (jika mereka tidak mengaktifkan CORS) itu tidak akan berfungsi; model keamanan internet melarangnya. Kecuali, tentu saja, Anda berada di dalam plugin chrome, dalam hal ini semuanya diizinkan - bahkan contoh di atas
Jared Forsyth
4
Anda harus img.src =mengikutinya img.onload =, karena di beberapa browser, seperti Opera, acara tidak akan terjadi.
ostapische
1
@ Hakkar kebocoran memori hanya akan terjadi di browser lama yang menggunakan penghitungan referensi diam untuk menginformasikan pengumpulan sampah. Sepengetahuan saya referensi melingkar tidak membuat kebocoran dalam pengaturan tanda dan menyapu . Begitu cakupan fungsi getBase64FromImageUrl(url)dan img.onload = function ()keluar, img tidak dapat dijangkau dan sampah dikumpulkan. Selain IE 6/7 dan ini ok.
humanityANDpeace
59

Datang lama setelah itu, tetapi tidak ada jawaban di sini yang sepenuhnya benar.

Saat digambar di atas kanvas, gambar yang dikirimkan tidak dikompresi + semua telah dikalikan.
Saat diekspor, tidak terkompresi atau dikompresi dengan algoritma yang berbeda, dan tidak dikalikan.

Semua browser dan perangkat akan memiliki kesalahan pembulatan yang berbeda terjadi dalam proses ini
(lihat Kanvas sidik jari ).

Jadi jika seseorang menginginkan versi base64 dari file gambar, mereka harus memintanya lagi (sebagian besar waktu itu akan berasal dari cache) tetapi kali ini sebagai gumpalan.

Kemudian Anda bisa menggunakan FileReader untuk membacanya baik sebagai ArrayBuffer, atau sebagai 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">

Kaiido
sumber
Baru saja melewati 5+ pertanyaan berbeda dan kode Anda adalah satu-satunya yang berfungsi dengan benar, terima kasih :)
Peter
1
Saya mendapatkan kesalahan ini dengan kode di atas. XMLHttpRequest cannot load data:image/jpeg;base64,/9j/4AA ... /2Q==. Invalid response. Origin 'https://example.com' is therefore not allowed access.
STWilson
2
@ STWilson, ya, metode ini juga terkait dengan kebijakan asal-sama, sama seperti kanvas. Dalam hal permintaan lintas asal, Anda perlu mengonfigurasi server hosting untuk mengizinkan permintaan lintas asal tersebut ke skrip Anda.
Kaiido
@ Kaiido jadi jika gambar di server lain dari skrip server hosting harus mengaktifkan permintaan lintas asal? Jika Anda mengatur img.setAttribute ('crossOrigin', 'anonymous') apakah itu mencegah kesalahan?
1,21 gigawatt
1
Setelah penelitian lebih lanjut, sepertinya gambar dapat menggunakan atribut crossOrigin untuk meminta akses tetapi terserah server hosting untuk memberikan akses melalui header CORS, sitepoint.com/an-in-depth-look-at-cors . Adapun XMLHttpRequest sepertinya server harus memberikan akses yang sama seperti untuk gambar melalui header CORS tetapi tidak ada perubahan yang diperlukan dalam kode Anda kecuali mungkin menetapkan xhr.withCredentials ke false (default).
1,21 gigawatt
20

Versi yang lebih modern dari jawaban Kaiido menggunakan fetch adalah:

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

Sunting: Seperti yang ditunjukkan dalam komentar, ini akan mengembalikan url objek yang menunjuk ke file di sistem lokal Anda alih-alih DataURL yang sebenarnya, jadi tergantung pada kasus penggunaan Anda, ini mungkin bukan yang Anda butuhkan.

Anda dapat melihat jawaban berikut untuk menggunakan pengambilan dan dataURUR yang sebenarnya: https://stackoverflow.com/a/50463054/599602

Zaptree
sumber
2
Jangan lupa menelepon URL.revokeObjectURL()jika Anda memiliki aplikasi yang berjalan lama menggunakan metode ini, atau memori Anda akan berantakan.
Engineer
1
Perhatian: DataURL (base64 disandikan) tidak sama dengan ObjectURL (referensi ke gumpalan)
DaniEll
1
ObjectURL tentu bukan URL data. Ini bukan jawaban yang benar.
Danny Lin
2

shiv / shim / sham

Jika gambar Anda sudah dimuat (atau tidak), "alat" ini mungkin berguna:

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

.. tapi kenapa?

Ini memiliki keuntungan menggunakan data gambar yang "sudah dimuat", jadi tidak diperlukan permintaan tambahan. Aditionally memungkinkan pengguna akhir (programmer seperti Anda) memutuskan CORS dan / atau mime-typedan quality-ATAU- Anda dapat meninggalkan argumen ini / parameter seperti yang dijelaskan dalam MDN spesifikasi di sini .

Jika JS Anda dimuat (sebelum saat diperlukan), maka mengonversi ke dataURLsesederhana:

contoh

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

Sidik jari GPU

Jika Anda khawatir tentang "ketepatan" bit maka Anda dapat mengubah alat ini sesuai dengan kebutuhan Anda sebagaimana disediakan oleh jawaban @ Kaiido.

argon
sumber
1

Gunakan onloadacara untuk mengonversi gambar setelah memuat

Kamil Kiełczewski
sumber
-3

Di HTML5 lebih baik gunakan ini:

{
//...
canvas.width = img.naturalWidth; //img.width;
canvas.height = img.naturalHeight; //img.height;
//...
}
KepHec
sumber
2
Saat menjawab pertanyaan cobalah untuk lebih spesifik dan memberikan penjelasan terperinci.
Piyush Zalani