Отримати URL-адресу зображень у JavaScript?


335

У мене є звичайна HTML-сторінка з деякими зображеннями (лише звичайні <img />теги HTML). Я хотів би отримати їхній вміст, бажано закодований base64, без необхідності перезавантажувати зображення (тобто воно вже завантажене браузером, тому зараз я хочу вміст).

Я хотів би досягти цього за допомогою Greasemonkey та Firefox.

Відповіді:


400

Примітка. Це працює лише в тому випадку, якщо зображення є з того самого домену, що і сторінка, або має crossOrigin="anonymous"атрибут і сервер підтримує CORS. Це також не дасть вам оригінальний файл, а перекодовану версію. Якщо вам потрібно, щоб результат був ідентичним оригіналу, дивіться відповідь Каїдо .


Вам потрібно буде створити елемент полотна з правильними розмірами і скопіювати дані зображення за допомогою drawImageфункції. Потім ви можете скористатися toDataURLфункцією для отримання даних: 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
Хоча це, здається, працює (за винятком незміненого / у відповідь), воно не створює ту саму рядок base64, як та, яку я отримую з PHP, коли роблю base64_encode у файлі, отриманому за допомогою функції file_get_contents. Зображення здаються дуже схожими / однаковими, все-таки у Javascripted менше, і я хотів би, щоб вони були абсолютно однаковими. Ще одне: вхідне зображення - невеликий (594 байт), 28х30 PNG з прозорим фоном - якщо це щось змінює.
Detariael

7
Firefox може використовувати інший рівень стиснення, який впливатиме на кодування. Крім того, я думаю, що PNG підтримує додаткову інформацію заголовка, як-от примітки, яка буде втрачена, оскільки полотно отримує лише дані пікселів. Якщо вам потрібно, щоб він був абсолютно таким же, ви, ймовірно, можете скористатися AJAX, щоб отримати файл і base64 кодувати його вручну.
Меттью Крамлі

7
Так, ви завантажите зображення за допомогою XMLHttpRequest. Сподіваємось, він використовував кешовану версію зображення, але це залежало б від конфігурації сервера та браузера, і ви матимете запит на голову, щоб визначити, чи змінився файл. Тому я не пропонував це в першу чергу :-) На жаль, наскільки я знаю, це єдиний спосіб отримати оригінальний файл.
Меттью Крамлі

2
@trusktr The drawImageнічого не зробить, і ви закінчите порожнім полотном і отриманим зображенням.
Меттью Крамлі

1
@JohnSewell Це лише тому, що ОП хотіла вмісту, а не URL-адреси. Вам потрібно буде пропустити виклик заміни (...), якщо ви хочете використовувати його як джерело зображення.
Меттью Крамлі

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), він не працюватиме; модель безпеки Інтернету відключає її. Якщо, звичайно, ви не знаходитесь у хромованому плагіні, і в цьому випадку все дозволено - навіть наведений вище приклад
Jared Forsyth

4
Ви повинні поставити img.src =після img.onload =, тому що в деяких браузерах, таких як Opera, подія не відбудеться.
ostapische

1
@Hakkar витік пам’яті відбуватиметься лише у старих браузерах, які використовують досі посилання для інформування про збирання сміття. Наскільки я розумію, циркулярна посилання не створює протікання в позначці та розгортці . Після того, як сфери функцій getBase64FromImageUrl(url)та img.onload = function ()виходу із зображення виходять, вони недоступні, а сміття збирається. Крім IE 6/7, і це нормально.
humanityANDpeace

59

Наближається довго, але жодна з відповідей тут не є абсолютно правильною.

Коли малюється на полотні, передане зображення не стискається + все попередньо множиться.
При експорті його нестискають або рекомпресують за допомогою іншого алгоритму та не розмножують.

Усі браузери та пристрої мають різні помилки округлення, що трапляються в цьому процесі
(див. Відбитки пальців на полотні ).

Тож якщо ви хочете базувати версію файлу зображень base64, вони повинні повторно запросити його (більшу частину часу він буде надходити з кешу), але цього разу як Blob.

Тоді ви можете використовувати FileReader, щоб прочитати його або як ArrayBuffer, або як даніURL.

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


Щойно пережив, як 5+ різних запитань, і ваш код єдиний, що правильно працював, дякую :)
Пітер

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

Більш сучасним варіантом відповіді кайді з використанням файлу буде:

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, тому залежно від випадку використання це може бути не тим, що вам потрібно.

Ви можете переглянути наступну відповідь на використання витягу та фактичних данихURL: https://stackoverflow.com/a/50463054/599602


2
Не забудьте зателефонувати, URL.revokeObjectURL()якщо у вас є давно запущена програма, що використовує цей метод, або ваша пам’ять забиється.
Інженер

1
Увага: DataURL (закодовано base64) не те саме, що ObjectURL (посилання на
блоб

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

Відбитки пальців графічного процесора

Якщо вас турбує "точність" бітів, ви можете змінити цей інструмент відповідно до ваших потреб, як це передбачено у відповіді @ Kaiido.


1

Використовуйте onloadподію для перетворення зображення після завантаження


-3

У HTML5 краще скористайтеся цим:

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

2
Відповідаючи на питання, спробуйте бути більш конкретними та надайте детальні пояснення.
Піюш Залані
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.