Получить URL-адрес данных изображения в JavaScript?

У меня есть обычная HTML-страница с некоторыми изображениями (просто обычные <img /> HTML-теги). Я хотел бы получить их контент, предпочтительно в кодировке base64, без необходимости повторно загружать изображение (т. Е. Оно уже загружено браузером, поэтому теперь мне нужен контент).

Я бы хотел добиться этого с помощью Greasemonkey и Firefox.


person Detariael    schedule 01.06.2009    source источник


Ответы (8)


Примечание. Это работает, только если изображение находится из того же домена, что и страница, или имеет атрибут crossOrigin="anonymous", а сервер поддерживает CORS. Он также не даст вам исходный файл, а даст вам перекодированную версию. Если вам нужно, чтобы результат был идентичен оригиналу, см. ответ Кайидо.


Вам нужно будет создать элемент холста с правильными размерами и скопировать данные изображения с помощью функции drawImage. Затем вы можете использовать функцию toDataURL для получения URL-адреса data: с изображением в кодировке base-64. Обратите внимание, что изображение должно быть полностью загружено, иначе вы просто получите пустое (черное, прозрачное) изображение.

Было бы что-то вроде этого. Я никогда не писал скрипт Greasemonkey, поэтому вам может потребоваться настроить код для работы в этой среде.

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

Получение изображения в формате JPEG не работает в более старых версиях (около 3.5) Firefox, поэтому, если вы хотите поддерживать это, вам необходимо проверить совместимость. Если кодировка не поддерживается, по умолчанию используется «image / png».

person Matthew Crumley    schedule 01.06.2009
comment
Хотя это, похоже, работает (за исключением неэкранированного / в возврате), он не создает ту же строку base64, что и та, которую я получаю от PHP, когда выполняю base64_encode в файле, полученном с помощью функции file_get_contents. Изображения кажутся очень похожими / одинаковыми, но Javascripted меньше, и я бы хотел, чтобы они были точно такими же. Еще одна вещь: входное изображение - это небольшой (594 байта) PNG 28x30 с прозрачным фоном - если это что-то меняет. - person Detariael; 01.06.2009
comment
Также motobit.com/util/base64-decoder-encoder.asp подтверждает, что строка в кодировке Base64 Javascript неверна - если она создана из исходного файла, результат будет таким же, как и у меня из PHP, и отличается от результата из Javascript. - person Detariael; 01.06.2009
comment
Firefox может использовать другой уровень сжатия, что повлияет на кодировку. Кроме того, я думаю, что PNG поддерживает некоторую дополнительную информацию заголовка, такую ​​как заметки, которые будут потеряны, поскольку холст получает только данные пикселей. Если вам нужно, чтобы он был точно таким же, вы, вероятно, могли бы использовать AJAX для получения файла, а base64 закодировал его вручную. - person Matthew Crumley; 01.06.2009
comment
Но что именно от AJAX мне нужно? XMLHttpRequest? Но разве это не то, что я пытаюсь сделать, - повторная загрузка файла? - person Detariael; 01.06.2009
comment
Да, вы бы загрузили изображение с помощью XMLHttpRequest. Надеюсь, он будет использовать кешированную версию изображения, но это будет зависеть от конфигурации сервера и браузера, и у вас будут накладные расходы на запрос, чтобы определить, изменился ли файл. Поэтому я и не предлагал этого в первую очередь :-) К сожалению, насколько мне известно, это единственный способ получить исходный файл. - person Matthew Crumley; 01.06.2009
comment
И если бы я решил использовать XMLHttpRequest (или его версию GM), что бы я сделал с результатом? Как преобразовать его в форму base64? Я думаю, Canvas не является ответом из-за проблем, описанных выше. Или, может быть, есть способ как-то сохранить файл на жесткий диск с помощью JavaScript (но я боюсь, что это невозможно из соображений безопасности)? Будет ли преобразование моего скрипта GM в расширение Fx или написание расширения Fx с нуля дать мне больше возможностей / доступа к файлам кеша каким-либо образом? - person Detariael; 01.06.2009
comment
Вам нужно будет использовать кодировщик base64, написанный на javascript. Если это найдено: webtoolkit.info/javascript-base64.html с помощью поиска в Google. Я никогда им не пользовался, но использовал другой код из webtoolkit.info. Что касается второй части, я не знаю, какие разрешения дает вам GM, но я знаю, что расширения могут читать и записывать файлы. - person Matthew Crumley; 01.06.2009
comment
Но мне все еще не хватает части от получения изображения с помощью XMLHttpRequest до передачи изображения какой-то функции Base64. Как я могу это сделать, по-прежнему используя Canvas, который оказался ненадежным? - person Detariael; 02.06.2009
comment
Я думаю, вы могли бы передать свойство xhr.reponseText кодировщику base64, когда запрос будет выполнен. - person Matthew Crumley; 02.06.2009
comment
Я решил отказаться от него, просто передав URL-адрес сценарию PHP, который будет запускаться через прокси-сервер кеширования, который также будет использовать браузер. Поскольку ваш код неплохо справлялся с этой задачей, я отмечаю это как ответ, даже если это не окончательное решение. - person Detariael; 02.06.2009
comment
Вопрос: Что произойдет, если вы попытаетесь сделать это до полной загрузки изображений? - person trusktr; 14.06.2012
comment
@trusktr drawImage ничего не сделает, и вы получите пустой холст и результирующее изображение. - person Matthew Crumley; 14.06.2012
comment
Я получаю эту ошибку. Любая идея? TypeError: значение не может быть преобразовано ни в одно из: HTMLImageElement, HTMLCanvasElement, HTMLVideoElement - person Sachin Prasad; 25.04.2013
comment
@SachinPrasad 웃 Возможно, вы захотите сделать это новым вопросом, но похоже, что любой элемент, который вы передаете в drawImage, не является изображением. - person Matthew Crumley; 25.04.2013
comment
@MatthewCrumley у меня есть этот код, но когда я применяю этот код в цикле, он возвращает Base64 после выполнения всего цикла. Я хочу, чтобы для каждого изображения он возвращал base64 одновременно .. как это будет возможно? - person Saurabh Android; 01.05.2014
comment
@SaurabhAndroid Похоже, вы хотите поместить результаты в массив и вернуть его. - person Matthew Crumley; 01.05.2014
comment
@MatthewCrumley, да именно так, как это будет возможно? - person Saurabh Android; 01.05.2014
comment
image/jpeg тоже действует! - person Meekohi; 21.05.2014
comment
Привет, я планирую использовать это в phonegap + angularjs, мой вопрос: нужно ли мне удалить созданный элемент document.createElement("canvas")? Я буду кодировать список из <img>, поэтому меня беспокоит объем памяти, который потребуется, если я создам список из canvas элемента. Ответьте, пожалуйста. Спасибо! - person fiberOptics; 25.09.2014
comment
@fiberOptics Нет, пока вы не храните никаких ссылок на холст, сборщик мусора должен позаботиться об этом за вас. - person Matthew Crumley; 25.09.2014
comment
Ммм ... похоже, ответ не работает с файлами GIF. - person Robert; 22.12.2014
comment
Вот другой подход, который может быть сделано без AngularJS; Я предполагаю, что это метод без потерь. - person Daan; 11.04.2015
comment
почему бы не использовать dataURL.replace(/^data:image\/([a-z]*);base64,/, "") - person sinujohn; 29.04.2015
comment
@sinu У меня, наверное, не было особой причины, так как это тоже работает. - person Matthew Crumley; 30.04.2015
comment
Зачем нужно заменять data: image / png, пожалуйста? Он не работает, если перешел на img.src - person John Sewell; 09.05.2016
comment
@JohnSewell Это только потому, что OP хотел контент, а не URL. Вам нужно будет пропустить вызов replace (...), если вы хотите использовать его в качестве источника изображения. - person Matthew Crumley; 09.05.2016
comment
Это работает, только если ваш источник изображения находится в том же домене (или у вас есть допустимые заголовки CORS). - person Sam Jason Braddock; 20.04.2017
comment
@SamJasonBraddock Это хороший аргумент. Не знаю, почему не упомянул об этом, но добавлю примечание. - person Matthew Crumley; 20.04.2017
comment
использование this.width и this.height даст вам размер изображения, соответствующий отображаемому элементу, но если само изображение больше, это приведет к тому, что на холсте будет отображаться только часть изображения. Чтобы исправить это, вы можете вместо этого использовать this.naturalHeight и this.naturalWitdth - person anyab; 26.06.2017

Эта функция принимает URL-адрес, а затем возвращает изображение 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;
}

Назовите это так: getBase64FromImageUrl("images/slbltxt.png")

person MuniR    schedule 15.05.2013
comment
есть ли способ превратить изображение с внешней ссылки на базу 64? - person dinodsaurus; 29.05.2013
comment
@dinodsaurus, вы можете загрузить его в тег изображения или выполнить запрос ajax. - person Jared Forsyth; 30.07.2013
comment
При этом я получаю неполное изображение. Вы знаете почему? - person emen; 20.09.2013
comment
@JaredForsyth, могу ли я узнать, что вы имеете в виду под запросом ajax? - person abc cba; 16.11.2013
comment
Что ж, для этого требуется, чтобы они реализовали CORS, но тогда вы просто выполняете $ .ajax (theimageurl), и он должен возвращать что-то разумное. В противном случае (если у них не включен CORS) это не сработает; модель безопасности Интернета запрещает это. Если, конечно, вы не находитесь внутри плагина Chrome, и в этом случае все разрешено - даже приведенный выше пример - person Jared Forsyth; 17.11.2013
comment
Вы должны поставить img.src = после img.onload =, потому что в некоторых браузерах, таких как Opera, событие не произойдет. - person ostapische; 26.12.2013
comment
@MuniR У меня есть этот код, но когда я применяю этот код в цикле, он возвращает Base64 после выполнения всего цикла. Я хочу, чтобы для каждого изображения он возвращал base64 одновременно .. как это будет возможно? - person Saurabh Android; 01.05.2014
comment
Saurabh Android. если вы имели в виду, что после завершения цикла вы получите только одно изображение, потому что каждый раз, когда функция заменяет URL-адрес, и поэтому остается последний URL-адрес, чтобы показать все изображения, вам нужно открыть их в новой вкладке вместо (.replace) - person MuniR; 03.09.2014
comment
Это приведет к утечке памяти, потому что событие img 'onload' никогда не отменяется. - person Hakkar; 16.02.2015
comment
@Hakkar утечка памяти произойдет только в старых браузерах, которые используют еще счетчик ссылок, чтобы сообщить о сборке мусора. Насколько я понимаю, циклическая ссылка не вызывает утечки в отметка и развертка. После выхода из областей видимости функций getBase64FromImageUrl(url) и img.onload = function () img становится недоступным и собирается мусор. Кроме IE 6/7, и это нормально. - person humanityANDpeace; 31.01.2016
comment
Спасибо за это. Однако сейчас мне нужно иметь несколько изображений. поэтому я передаю массив URL-адресов изображения и зацикливаю один за другим, чтобы сгенерировать URL-адрес данных. Проблема в том, что когда он создает холст для первого изображения, затем второй создает холст, и он заменяет первое изображение, и это становится беспорядком. Я перешел на использование setInterval, чтобы дать немного времени для создания холста и генерации URL-адреса данных. но не работают в продакшене. Какие-нибудь решения для этого? - person chourn solidet; 05.05.2017

Придет много времени спустя, но ни один из ответов здесь не является полностью правильным.

При рисовании на холсте переданное изображение несжатое + все предварительно умножается.
При экспорте оно не сжимается или повторно сжимается с помощью другого алгоритма и не умножается.

Все браузеры и устройства будут иметь разные ошибки округления в этом процессе
(см. Canvas fingerprinting) .

Поэтому, если кому-то нужна версия файла изображения в формате base64, они должны снова запросить ее (в большинстве случаев она будет поступать из кеша), но на этот раз как Blob.

Затем вы можете использовать FileReader, чтобы читать его как ArrayBuffer или как 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">

person Kaiido    schedule 21.03.2017
comment
Только что ответил на более чем 5 разных вопросов, и ваш код - единственный, который работал правильно, спасибо :) - person Peter; 26.03.2017
comment
Я получаю эту ошибку с приведенным выше кодом. XMLHttpRequest cannot load data:image/jpeg;base64,/9j/4AA ... /2Q==. Invalid response. Origin 'https://example.com' is therefore not allowed access. - person STWilson; 02.05.2017
comment
@STWilson, да, этот метод тоже привязан к политике того же происхождения, как и метод холста. В случае запроса из разных источников вам необходимо настроить сервер хостинга, чтобы разрешить такие запросы из разных источников в ваш скрипт. - person Kaiido; 03.05.2017
comment
@Kaiido, значит, если изображение находится на другом сервере, чем сценарий, сервер хостинга должен разрешать запросы с перекрестным происхождением? Если вы установите img.setAttribute ('crossOrigin', 'anonymous'), предотвратит ли это ошибку? - person 1.21 gigawatts; 14.05.2017
comment
После дополнительных исследований выяснилось, что изображения могут использовать атрибут crossOrigin для запроса доступа, но хостинг-сервер должен предоставить доступ через заголовок CORS, sitepoint.com/an-in-depth-look-at-cors. Что касается XMLHttpRequest, похоже, что сервер должен предоставлять доступ так же, как и для изображений, через заголовок CORS, но никаких изменений в вашем коде не требуется, за исключением, возможно, установки xhr.withCredentials в false (по умолчанию). - person 1.21 gigawatts; 15.05.2017
comment
Дословно скопировал ваш код, и я получаю эту ошибку Uncaught ReferenceError: result is not defined - person jkupczak; 01.07.2017
comment
@jkupczak result - это id второго элемента изображения. Подобно document.getElementById('result'), но с не очень хорошо написанным синтаксисом. - person Kaiido; 02.07.2017
comment
еще раз спасибо за еще один отличный ответ. что, если вам не нужна версия base64, а просто нужен dataURI для сериализации SVG? вам все равно нужно перезагрузить изображение, даже если оно уже загружено на страницу? - person Crashalot; 01.01.2019
comment
@Crashalot, тогда вам нужна версия base64, и нет, технически вам не нужно перезагружать изображение, только чтобы получить его снова (вся часть декодирования для загрузки изображения здесь не требуется), и эта выборка в большинстве случаев будет просто захватом из кеша, так что нового сетевого запроса тоже не будет. - person Kaiido; 01.01.2019
comment
чтобы прояснить для будущих читателей, вы советуете людям использовать средство чтения файлов, а не холст, для получения URL-адресов данных, поскольку холст может изменять изображение, а средство чтения файлов - нет. верный? - person Crashalot; 31.07.2020

Более современная версия ответа Кайдо с использованием выборки будет:

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

Изменить: как указано в комментариях, это вернет URL-адрес объекта, который указывает на файл в вашей локальной системе, вместо фактического DataURL, поэтому в зависимости от вашего варианта использования это может быть не то, что вам нужно.

Вы можете посмотреть следующий ответ, чтобы использовать выборку и фактический dataURL: https://stackoverflow.com/a/50463054/599602

person Zaptree    schedule 21.07.2017
comment
Не забудьте вызвать URL.revokeObjectURL(), если у вас есть долгое время работы приложения, использующего этот метод, иначе ваша память будет захламлена. - person Engineer; 30.12.2018
comment
Внимание: DataURL (в кодировке base64) не то же самое, что ObjectURL (ссылка на blob) - person DaniEll; 14.10.2019
comment
ObjectURL определенно не является URL-адресом данных. Это неправильный ответ. - person Danny Lin; 08.12.2019

шайба / шайба / подделка

Если ваши изображения уже загружены (или нет), этот "инструмент" может пригодиться:

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

.. но почему?

Это дает преимущество использования «уже загруженных» данных изображения, поэтому дополнительный запрос не требуется. Кроме того, он позволяет конечному пользователю (программисту вроде вас) выбирать CORS и / или mime-type и quality -ИЛИ- вы можете не указывать эти аргументы / параметры, как описано в MDN спецификацию здесь.

Если у вас загружен этот JS (до того момента, когда он понадобится), преобразование в dataURL так же просто, как:

Примеры

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

Снятие отпечатков с графического процессора

Если вас беспокоит «точность» битов, вы можете изменить этот инструмент в соответствии со своими потребностями, как указано в ответе @Kaiido.

person argon    schedule 01.11.2019
comment
Прохладный!!! Так полезно. Это должно быть поставлено на первое место. - person mika; 24.02.2021
comment
Отличный подход! Хорошо сказано. - person Fadi Chamieh; 29.05.2021

Используйте событие onload для преобразования изображения после загрузки

function loaded(img) {
  let c = document.createElement('canvas')
  c.getContext('2d').drawImage(img, 0, 0)
  msg.innerText= c.toDataURL();
}
pre { word-wrap: break-word; width: 500px; white-space: pre-wrap; }
<img onload="loaded(this)" src="https://cors-anywhere.herokuapp.com/http://lorempixel.com/200/140" crossorigin="anonymous"/>

<pre id="msg"></pre>

person Kamil Kiełczewski    schedule 24.04.2019

Это все, что вам нужно прочитать.

https://developer.mozilla.org/en-US/docs/Web/API/FileReader/readAsBinaryString

var height = 200;
var width  = 200;

canvas.width  = width;
canvas.height = height;

var ctx = canvas.getContext('2d');

ctx.strokeStyle = '#090';
ctx.beginPath();
ctx.arc(width/2, height/2, width/2 - width/10, 0, Math.PI*2);
ctx.stroke();

canvas.toBlob(function (blob) {
  //consider blob is your file object

  var reader = new FileReader();

  reader.onload = function () {
    console.log(reader.result);
  }

  reader.readAsBinaryString(blob);
});
person ashdaily    schedule 16.07.2020

В HTML5 лучше использовать это:

{
//...
canvas.width = img.naturalWidth; //img.width;
canvas.height = img.naturalHeight; //img.height;
//...
}
person KepHec    schedule 12.01.2019
comment
Отвечая на вопрос, постарайтесь быть более конкретным и дать подробные объяснения. - person Piyush Zalani; 12.01.2019
comment
Спасибо, что указали на это! Я всегда получаю обрезанное изображение, даже не зная об этом. - person mika; 24.02.2021