web-dev-qa-db-ja.com

データURIのajaxリクエストからの生画像データを使用する

AjaxとデータURIの組み合わせを使用してJPEGイメージをロードし、単一のHTTP要求でそのEXIFデータを抽出しようとしています。これを行うためにライブラリを変更しています( https://github.com/kennydude/photosphere )。現在、このライブラリは2つの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-encoded-then-unencoded-again生データの長さ(バイト単位)が同じであるように見えます。ただし、画像srcを設定しようとすると、Firefox(25)とChrome(31)(現在のバージョン)の両方で失敗します-chromeは「壊れた画像」アイコンを表示しますsrcが無効な形式であることを示唆しています。

Base64のエンコード/デコードに関する情報は、このmozillaページを使用しました。

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

何が間違っているのでしょうか?辺りを見回すと、base64でエンコードされた画像サーバー側を作成できますが、このようにクライアント側で実行できますか? 1つには、base64エンコードサーバー側では明らかにデータサイズが増加し、この演習の全体的な目的は、サーバーから転送されるデータの量とリクエスト数を削減することです。

ありがとう、ニック

36
nickw

ありがとう。私はこれをもう少し掘り下げましたが、少なくとも現在のバージョンの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>

基本的には、バイナリ応答を要求し、データを(バイナリフレンドリーな)文字列String.fromCharCode()に戻す前に、データの8ビット符号なしintビューを作成します。 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/

ニック

33
nickw

ニックの答えはとてもうまくいきます。しかし、かなり大きなファイルでこれを行うと、スタックオーバーフローが発生しました。

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

上記のArrayBuffer -> String -> Base64メソッドに問題がありましたが、 Blob を使用して別のメソッドに遭遇しました。それはnot生データをBase 64に変換する方法(タイトルのように)ですが、それはis生の画像データを表示する方法(実際の質問のように):

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

すべてのクレジットは this fiddle の著者であるJan Miksovskyに送られます。私はそれを偶然見つけて、この議論に役立つ追加になると考えました。

15
Xavier Holt

ResponseTextは文字列として扱われ、サーバーが送信する応答データはバイナリであるため、サーバー側でbase64エンコードを行う必要があります。

0
Shreyas

画像ダウンロード用の最新の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)
0
icl7126

Microsoft Graftから受け取った生データからユーザーのOutlookプロファイル画像をレンダリングするソリューションが必要だったため、この問題に2日間取り組んできました。上記のすべてのソリューションを実装しましたが、成功しませんでした。その後、私はこのgitを見つけました: jquery ajaxを使用してresponseBodyから画像のbase64生データを取得

私の場合、"data:image/png;base64," with "data:image/jpg;base64,"

それは魅力のように機能します。

0
KingofDendroar