Использование необработанных данных изображения из запроса AJAX для URI данных

Я пытаюсь использовать комбинацию Ajax и URI данных, чтобы загрузить изображение JPEG и извлечь его данные EXIF ​​с помощью одного HTTP-запроса. Я изменяю библиотеку ( https://github.com/kennydude/photosphere) для этого; В настоящее время эта библиотека использует два HTTP-запроса, чтобы установить источник изображения и получить данные EXIF.

Получение EXIF ​​работает, никаких проблем. Однако у меня возникают трудности с использованием необработанных данных из запроса AJAX в качестве источника для изображения.

Исходный код для небольшого теста методики:

<!DOCTYPE html>
<html>
<head>
<script type='text/javascript'>

function init()
{
    // own ajax library - using it to request a test jpg image
    new Ajax().sendRequest
    (
        "/images/photos/badger.jpg",
         { method : "GET",
            callback: function(xmlHTTP)
            {

                var encoded = btoa (unescape(encodeURIComponent(xmlHTTP.responseText)));
                var dataURL="data:image/jpeg;base64,"+encoded;

                document.getElementById("image").src = dataURL;
            }
        }
    );
}

</script>
<script type="text/javascript" src="http://www.free-map.org.uk/0.6/js/lib/Ajax.js"></script>
</head>
<body onload='init()'>
<img id="image" alt="data url loaded image" />
</body>
</html>

Я получаю то, что выглядит как разумные данные в формате JPEG, отправленные обратно, и длина (в байтах) необработанных данных и необработанных данных, закодированных в base64, а затем еще не закодированных, одинакова. Однако попытка установить изображение src не удалась как в Firefox (25), так и в Chrome (31) (текущие версии) - в chrome отображается значок "испорченное изображение", указывающий на то, что src является недопустимым форматом.

Я использовал эту страницу Mozilla для информации о кодировке / декодировании base64:

https://developer.mozilla.org/en-US/docs/Web/JavaScript/Base64_encoding_and_decoding

Есть идеи, что может быть не так? Оглядываясь вокруг, я могу создать сторону сервера кодированных изображений base64, но можно ли это сделать на стороне клиента, как это? С одной стороны, сторона сервера кодирования base64, очевидно, увеличивает размер данных, и вся цель этого упражнения - сократить объем данных, передаваемых с сервера, а также количество запросов.

Спасибо ник

6 ответов

Спасибо за это. Я немного покопался в этом, и оказалось, что есть решение по крайней мере для текущих версий Firefox и Chrome (EDIT: IE10 тоже работает). Вы можете использовать XMLHttpRequest2 и использовать типизированный массив (Uint8Array). Следующий код работает:

<!DOCTYPE html>
<html>
<head>
<script type='text/javascript'>

function init()
{
    var xmlHTTP = new XMLHttpRequest();
    xmlHTTP.open('GET','/images/photos/badger.jpg',true);

    // Must include this line - specifies the response type we want
    xmlHTTP.responseType = 'arraybuffer';

    xmlHTTP.onload = function(e)
    {

        var arr = new Uint8Array(this.response);


        // Convert the int array to a binary string
        // We have to use apply() as we are converting an *array*
        // and String.fromCharCode() takes one or more single values, not
        // an array.
        var raw = String.fromCharCode.apply(null,arr);

        // This works!!!
        var b64=btoa(raw);
        var dataURL="data:image/jpeg;base64,"+b64;
        document.getElementById("image").src = dataURL;
    };

    xmlHTTP.send();
}

</script>
</head>
<body onload='init()'>
<img id="image" alt="data url loaded image" />
</body>
</html>

По сути, вы запрашиваете двоичный ответ, затем создаете 8-разрядное представление без знака int для данных перед преобразованием их обратно в (двоичную) строку String.fromCharCode(). Применение необходимо, поскольку String.fromCharCode () не принимает аргумент массива. Затем вы используете btoa(), создаете URL-адрес данных, и он работает.

Следующие ресурсы были полезны для этого:

https://developer.mozilla.org/en-US/docs/Web/JavaScript/Typed_arrays?redirectlocale=en-US&redirectslug=JavaScript%2FTyped_arrays

а также

http://www.html5rocks.com/en/tutorials/file/xhr2/

Ник

Ответ Ника работает очень хорошо. Но когда я сделал это с довольно большим файлом, я получил переполнение стека

var raw = String.fromCharCode.apply(null,arr);

Генерация сырой строки кусками работала хорошо для меня.

var raw = '';
var i,j,subArray,chunk = 5000;
for (i=0,j=arr.length; i<j; i+=chunk) {
   subArray = arr.subarray(i,i+chunk);
   raw += String.fromCharCode.apply(null, subArray);
}

У меня были проблемы с ArrayBuffer -> String -> Base64 метод, описанный выше, но натолкнулся на другой метод с использованием Blob, который отлично работал. Это не способ преобразования необработанных данных в Base 64 (как в заголовке), но это способ отображения необработанных данных изображения (как в реальном вопросе):

var xhr = new XMLHttpRequest();
xhr.responseType = 'arraybuffer';
xhr.onload = function() {
    var blb = new Blob([xhr.response], {type: 'image/png'});
    var url = (window.URL || window.webkitURL).createObjectURL(blb);
    image.src = url;
}

xhr.open('GET', 'http://whatever.com/wherever');
xhr.send();

Вся заслуга Яна Миксовского, автора этой скрипки. Я просто наткнулся на это и подумал, что это станет полезным дополнением к этой дискуссии.

Современное решение ES6 для загрузки изображений: (без указания типа изображения)

async function downloadImageFromUrl(url) {    // returns dataURL
  const xmlHTTP = new XMLHttpRequest();
  xmlHTTP.open('GET', url, true);
  xmlHTTP.responseType = 'blob';
  const imageBlob = await new Promise((resolve, reject) => {
    xmlHTTP.onload = e => xmlHTTP.status >= 200 && xmlHTTP.status < 300 && xmlHTTP.response.type.startsWith('image/') ? resolve(xmlHTTP.response) : reject(Error(`wrong status or type: ${xmlHTTP.status}/${xmlHTTP.response.type}`));
    xmlHTTP.onerror = reject;
    xmlHTTP.send();
  });
  return blobToDataUrl(imageBlob);
}

function blobToDataUrl(blob) { return new Promise(resolve => {
  const reader = new FileReader();    // https://developer.mozilla.org/en-US/docs/Using_files_from_web_applications
  reader.onload = e => resolve(e.target.result);
  reader.readAsDataURL(blob);
})}

Использование:

downloadImageFromUrl('https://a.b/img.png').then(console.log, console.error)

Я работал над этой проблемой в течение двух дней, так как мне нужно было решение для визуализации пользовательского изображения профиля Outlook из необработанных данных, полученных от Microsoft Graft. Я реализовал все решения выше, но безуспешно. Затем я нашел этот мерзавец: получить base64 необработанные данные изображения из responseBody, используя jquery ajax

В моем случае я просто заменил "data:image/png;base64," с "data:image/jpg;base64,"

Отлично работает.

Вам нужно будет выполнить кодирование base64 на стороне сервера, поскольку responseText обрабатывается как строка, а данные ответа, отправляемые сервером, являются двоичными.

Другие вопросы по тегам