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

335

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

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

Detariael
источник

Ответы:

400

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


Вам нужно будет создать элемент canvas с правильными размерами и скопировать данные изображения с помощью drawImageфункции. Затем вы можете использовать toDataURLфункцию для получения data: url с закодированным изображением 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».

Мэтью Крамли
источник
1
Хотя кажется, что это работает (за исключением unescaped / in return), он не создает ту же строку base64, что и строка, которую я получаю из PHP при выполнении base64_encode для файла, полученного с помощью функции file_get_contents. Изображения кажутся очень похожими / одинаковыми, но Javascript-код меньше, и я бы хотел, чтобы они были такими же. Еще одна вещь: входное изображение - маленький (594 байта), PNG 28x30 с прозрачным фоном - если это что-то меняет.
Detariael
7
Firefox может использовать другой уровень сжатия, который повлияет на кодировку. Кроме того, я думаю, что PNG поддерживает некоторую дополнительную информацию заголовка, такую ​​как заметки, которая будет потеряна, поскольку холст получает только данные пикселей. Если вам нужно, чтобы он был точно таким же, вы, вероятно, могли бы использовать AJAX для получения файла, а base64 закодировать его вручную.
Мэтью Крамли
7
Да, вы бы загрузили изображение с помощью XMLHttpRequest. Надеемся, что он будет использовать кэшированную версию изображения, но это будет зависеть от конфигурации сервера и браузера, и у вас будут накладные расходы на запрос, чтобы определить, изменился ли файл. Вот почему я не предлагал этого в первую очередь :-) К сожалению, насколько я знаю, это единственный способ получить оригинальный файл.
Мэтью Крамли
2
@trusktr The drawImageничего не сделает, и вы получите пустой холст и получившееся изображение.
Мэтью Крамли
1
@JohnSewell Это только потому, что ОП хотел контент, а не URL. Вам нужно будет пропустить вызов replace (...), если вы хотите использовать его в качестве источника изображения.
Мэтью Крамли
76

Эта функция берет 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")

Мунир
источник
8
Есть ли способ превратить изображение с внешней ссылки на базу 64?
динозавр
@dinodsaurus вы можете загрузить его в тег изображения или сделать запрос ajax.
Джаред Форсайт
6
Ну, это требует от них реализации CORS, но тогда вы просто делаете $ .ajax (theimageurl), и он должен возвращать что-то разумное. В противном случае (если у них не включен CORS), это не сработает; модель безопасности Интернета запрещает это. Если, конечно, вы не находитесь в плагине Chrome, в этом случае все разрешено - даже приведенный выше пример
Джаред Форсайт
4
Вы должны поставить img.src =после img.onload =, потому что в некоторых браузерах, таких как Opera, событие не произойдет.
Остапище
1
@Hakkar утечка памяти будет происходить только в старых браузерах, которые все еще используют счетчик ссылок для информирования о сборке мусора. Насколько я понимаю, круговая ссылка не создает утечки в настройке метки и развертки . После выхода из функций getBase64FromImageUrl(url)и img.onload = function ()выхода из них img становится недоступным, а сборщик мусора. Кроме IE 6/7 и это нормально.
человечествоANDpeace
59

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

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

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

Поэтому, если кто-то хочет версию образа для 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">

Kaiido
источник
Просто перебрал 5+ разных вопросов, и ваш код - единственный, который работал правильно, спасибо :)
Peter
1
Я получаю эту ошибку с кодом выше. XMLHttpRequest cannot load data:image/jpeg;base64,/9j/4AA ... /2Q==. Invalid response. Origin 'https://example.com' is therefore not allowed access.
STWilson
2
@STWilson, да, этот метод также связан с политикой того же происхождения, как и холст. В случае запроса кросс-источника вам необходимо настроить сервер-хостинг, чтобы разрешить такие запросы кросс-источника к вашему сценарию.
Кайидо
@ Kaiido, поэтому, если изображение находится на другом сервере, чем сценарий, на котором хост-сервер должен разрешать перекрестные запросы? Если вы установите img.setAttribute ('crossOrigin', 'anonymous'), это предотвратит ошибку?
1,21 гигаватта
1
После дополнительных исследований, похоже, что изображения могут использовать атрибут crossOrigin для запроса доступа, но хостинг-сервер может предоставить доступ через заголовок CORS, sitepoint.com/an-in-depth-look-at-cors . Что касается XMLHttpRequest, похоже, что сервер должен предоставлять доступ так же, как для изображений через заголовок CORS, но никаких изменений в вашем коде не требуется, за исключением, возможно, установки xhr.withCredentials в значение false (по умолчанию).
1,21 гигаватта
20

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

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, поэтому в зависимости от вашего варианта использования это может быть не то, что вам нужно.

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

Zaptree
источник
2
Не забудьте позвонить, URL.revokeObjectURL()если у вас есть долго работающее приложение, использующее этот метод, или ваша память будет загромождена.
инженер
1
Внимание: DataURL (в кодировке base64) отличается от ObjectURL (ссылка на BLOB-
объект
1
ObjectURL, конечно, не URL-адрес данных. Это не правильный ответ.
Дэнни Лин
2

Шив / Шим / Шам

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

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

.. но почему?

Это дает преимущество использования «уже загруженных» данных изображения, поэтому дополнительный запрос не требуется. Aditionally это позволяет конечному пользователю (программисту , как вы) принимает решение о 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());

Снятие отпечатков пальцев GPU

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

аргон
источник
1

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

Камил Келчевски
источник
-3

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

{
//...
canvas.width = img.naturalWidth; //img.width;
canvas.height = img.naturalHeight; //img.height;
//...
}
KepHec
источник
2
Отвечая на вопрос, постарайтесь быть более конкретным и дать подробные объяснения.
Пиюш Залани