web-dev-qa-db-fra.com

Utilisation de données d'image brutes à partir d'une demande ajax pour l'URI de données

J'essaie d'utiliser une combinaison d'URI Ajax et de données pour charger une image JPEG et extraire ses données EXIF ​​avec une seule requête HTTP. Je modifie une bibliothèque ( https://github.com/kennydude/photosphere ) pour le faire; Actuellement, cette bibliothèque utilise deux requêtes HTTP pour définir la source de l'image et obtenir les données EXIF.

Obtenir les EXIF ​​fonctionne, pas de problème. Cependant, j'ai des difficultés à utiliser les données brutes de la demande ajax comme source pour l'image.

Code source pour un petit test de la technique:

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

Je reçois ce qui ressemble à des données jpeg sensibles renvoyées, et la longueur (en octets) des données brutes et des données brutes codées en base64, puis non codées, est la même. Toutefois, la tentative de définition de l'image src échoue à la fois sur Firefox (25) et Chrome (31) (versions actuelles) - chrome affiche l'icône "image endommagée", ce qui suggère que src est un format non valide. 

J'ai utilisé cette page mozilla pour des informations sur l'encodage/décodage en base64:

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

Une idée de ce qui pourrait être faux? En regardant autour de moi, je peux créer le côté serveur d'images codé en Base64, mais est-ce possible de le faire côté client? D'une part, l'encodage en base64 côté serveur augmente évidemment la taille des données et le but de cet exercice est de réduire la quantité de données transférées depuis le serveur, ainsi que le nombre de requêtes.

Merci, Nick

36
nickw

Merci pour ça. J'ai creusé un peu plus à ce sujet et il s'avère qu'il existe au moins une solution pour les versions actuelles de Firefox et Chrome (EDIT: IE10 fonctionne également). Vous pouvez utiliser XMLHttpRequest2 et utiliser un tableau typé (Uint8Array). Le code suivant fonctionne:

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

En gros, vous demandez une réponse binaire, puis créez une vue 8 bits non signée des données avant de la reconvertir en chaîne (chaîne binaire) String.fromCharCode (). Le paramètre apply est nécessaire car String.fromCharCode () n'accepte pas d'argument de tableau. Vous utilisez ensuite btoa (), créez votre URL de données et cela fonctionne.

Les ressources suivantes ont été utiles pour cela:

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

et

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

Entaille

32
nickw

La réponse de Nick fonctionne très bien. Mais quand j'ai fait cela avec un fichier assez volumineux, j'ai eu un débordement de pile 

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

La génération de la chaîne brute en morceaux a bien fonctionné pour moi.

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

J'ai eu des problèmes avec la méthode ArrayBuffer -> String -> Base64 décrite ci-dessus, mais j'ai rencontré une autre méthode utilisant Blob qui a très bien fonctionné. C'est pas un moyen de convertir des données brutes en Base 64 (comme dans le titre), mais il est un moyen d'afficher des données d'image brutes (comme dans la question):

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

Tout le mérite revient à Jan Miksovsky, auteur de ce violon . Je suis tombé dessus et j'ai pensé que cela contribuerait utilement à cette discussion.

15
Xavier Holt

Vous devrez coder en base64 côté serveur car le responseText est traité comme une chaîne et les données de réponse envoyées par le serveur sont binaires. 

0
Shreyas

Solution moderne pour le téléchargement d'images utilisant l'ES6: (sans spécifier le type d'image)

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

Utilisation:

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

Cela fait deux jours que je travaille sur ce problème car j'avais besoin d'une solution pour restituer l'image de profil Outlook de l'utilisateur à partir des données brutes reçues de Microsoft Graft. J'ai mis en œuvre toutes les solutions ci-dessus, sans succès. Puis j'ai trouvé ce git: obtenir les données brutes base64 de l’image de responseBody en utilisant jquery ajax

Dans mon cas, je viens de remplacer "data:image/png;base64," par "data:image/jpg;base64,"

Il fonctionne comme un charme.

0
KingofDendroar