web-dev-qa-db-ja.com

データURIをファイルに変換してからFormDataに追加する

私はHTML5画像アップローダを再実装しようとしています Mozilla Hacks サイトの上にありますが、それはWebKitブラウザで動作します。タスクの一部は、canvasオブジェクトからイメージファイルを抽出し、それをアップロード用に FormData オブジェクトに追加することです。

問題は、canvasには画像ファイルの表現を返すtoDataURL関数があるのに対し、FormDataオブジェクトは File API からのFileまたはBlobオブジェクトしか受け付けないことです。

Mozillaソリューションはcanvasに対して次のFirefox専用の関数を使用しました。

var file = canvas.mozGetAsFile("foo.png");

...これはWebKitブラウザでは利用できません。考えられる最善の解決策は、Data URIをFileオブジェクトに変換する方法を見つけることです。これはFile APIの一部と考えられますが、それを実現するための何かを見つけることはできません。

出来ますか?そうでない場合は、代替案はありますか?

ありがとう。

263
Stoive

いくつかのことで遊んだ後、私はこれを自分で理解することができました。

まず最初に、これはdataURIをBlobに変換します。

function dataURItoBlob(dataURI) {
    // convert base64/URLEncoded data component to raw binary data held in a string
    var byteString;
    if (dataURI.split(',')[0].indexOf('base64') >= 0)
        byteString = atob(dataURI.split(',')[1]);
    else
        byteString = unescape(dataURI.split(',')[1]);

    // separate out the mime component
    var mimeString = dataURI.split(',')[0].split(':')[1].split(';')[0];

    // write the bytes of the string to a typed array
    var ia = new Uint8Array(byteString.length);
    for (var i = 0; i < byteString.length; i++) {
        ia[i] = byteString.charCodeAt(i);
    }

    return new Blob([ia], {type:mimeString});
}

そこから、ファイルとしてアップロードされるようにデータをフォームに追加するのは簡単です。

var dataURL = canvas.toDataURL('image/jpeg', 0.5);
var blob = dataURItoBlob(dataURL);
var fd = new FormData(document.forms[0]);
fd.append("canvasImage", blob);
437
Stoive

BlobBuilderとArrayBufferは廃止予定となりました。Blobコンストラクタで更新されたトップコメントのコードは次のとおりです。

function dataURItoBlob(dataURI) {
    var binary = atob(dataURI.split(',')[1]);
    var array = [];
    for(var i = 0; i < binary.length; i++) {
        array.Push(binary.charCodeAt(i));
    }
    return new Blob([new Uint8Array(array)], {type: 'image/jpeg'});
}
139
vava720

これはiOSとSafariで動作します。

StoiveのArrayBufferソリューションを使用する必要がありますが、vava720が示すようにBlobBuilderを使用することはできません。そのため、両方のマッシュアップを次に示します。

function dataURItoBlob(dataURI) {
    var byteString = atob(dataURI.split(',')[1]);
    var ab = new ArrayBuffer(byteString.length);
    var ia = new Uint8Array(ab);
    for (var i = 0; i < byteString.length; i++) {
        ia[i] = byteString.charCodeAt(i);
    }
    return new Blob([ab], { type: 'image/jpeg' });
}
52
William T.

Firefoxには canvas.toBlob() および canvas.mozGetAsFile() メソッドがあります。

しかし、他のブラウザはそうではありません。

キャンバスからdataurlを取得してから、dataurlをBLOBオブジェクトに変換できます。

これが私のdataURLtoBlob()関数です。とても短いです。

function dataURLtoBlob(dataurl) {
    var arr = dataurl.split(','), mime = arr[0].match(/:(.*?);/)[1],
        bstr = atob(arr[1]), n = bstr.length, u8arr = new Uint8Array(n);
    while(n--){
        u8arr[n] = bstr.charCodeAt(n);
    }
    return new Blob([u8arr], {type:mime});
}

キャンバスまたはデータを処理するには、FormDataとともにこの関数を使用します。

例えば:

var dataurl = canvas.toDataURL('image/jpeg',0.8);
var blob = dataURLtoBlob(dataurl);
var fd = new FormData();
fd.append("myFile", blob, "thumb.jpg");

また、非geckoエンジンのブラウザ用にHTMLCanvasElement.prototype.toBlobメソッドを作成することもできます。

if(!HTMLCanvasElement.prototype.toBlob){
    HTMLCanvasElement.prototype.toBlob = function(callback, type, encoderOptions){
        var dataurl = this.toDataURL(type, encoderOptions);
        var bstr = atob(dataurl.split(',')[1]), n = bstr.length, u8arr = new Uint8Array(n);
        while(n--){
            u8arr[n] = bstr.charCodeAt(n);
        }
        var blob = new Blob([u8arr], {type: type});
        callback.call(this, blob);
    };
}

canvas.toBlob()はFirefoxだけでなく最近のすべてのブラウザで動作します。例えば:

canvas.toBlob(
    function(blob){
        var fd = new FormData();
        fd.append("myFile", blob, "thumb.jpg");
        //continue do something...
    },
    'image/jpeg',
    0.8
);
25
cuixiping

私の好きな方法は canvas.toBlob()

しかし、とにかく、ここでもfetchを使ってbase64をBLOBに変換するもう1つの方法があります^^、

var url = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAUAAAAFCAYAAACNbyblAAAAHElEQVQI12P4//8/w38GIAXDIBKE0DHxgljNBAAO9TXL0Y4OHwAAAABJRU5ErkJggg=="

fetch(url)
.then(res => res.blob())
.then(blob => {
  var fd = new FormData()
  fd.append('image', blob, 'filename')
  
  console.log(blob)

  // Upload
  // fetch('upload', {method: 'POST', body: fd})
})
21
Endless

@Stoiveと@ vava720のおかげで、この2つを組み合わせて、廃止予定のBlobBuilderとArrayBufferを使用しないようにしました。

function dataURItoBlob(dataURI) {
    'use strict'
    var byteString, 
        mimestring 

    if(dataURI.split(',')[0].indexOf('base64') !== -1 ) {
        byteString = atob(dataURI.split(',')[1])
    } else {
        byteString = decodeURI(dataURI.split(',')[1])
    }

    mimestring = dataURI.split(',')[0].split(':')[1].split(';')[0]

    var content = new Array();
    for (var i = 0; i < byteString.length; i++) {
        content[i] = byteString.charCodeAt(i)
    }

    return new Blob([new Uint8Array(content)], {type: mimestring});
}
19
Mimo

Mozillaの推測では危険にさらされているため、進化する標準は canvas.toBlob() canvas.getAsFile()ではないようです。

私はまだそれをサポートしているブラウザを見ていません:(

この素晴らしいスレッドをありがとう!

また、支持を限定的(そして名前空間付き)にしているので、受け入れられた答えを試みる人は誰でもBlobBuilderに注意するべきです:

    var bb;
    try {
        bb = new BlobBuilder();
    } catch(e) {
        try {
            bb = new WebKitBlobBuilder();
        } catch(e) {
            bb = new MozBlobBuilder();
        }
    }

BlobBuilderに別のライブラリのポリフィルを使用しましたか?

12
Chris Bosco
var BlobBuilder = (window.MozBlobBuilder || window.WebKitBlobBuilder || window.BlobBuilder);

try catchなしで使用できます。

Check_caに感謝します。すごい仕事。

5
Nafis Ahmad

Stoiveによる最初の答えは、最後の行をBlobに合わせて変更することで簡単に修正できます。

function dataURItoBlob (dataURI) {
    // convert base64 to raw binary data held in a string
    // doesn't handle URLEncoded DataURIs
    var byteString;
    if (dataURI.split(',')[0].indexOf('base64') >= 0)
        byteString = atob(dataURI.split(',')[1]);
    else
        byteString = unescape(dataURI.split(',')[1]);
    // separate out the mime component
    var mimeString = dataURI.split(',')[0].split(':')[1].split(';')[0];

    // write the bytes of the string to an ArrayBuffer
    var ab = new ArrayBuffer(byteString.length);
    var ia = new Uint8Array(ab);
    for (var i = 0; i < byteString.length; i++) {
        ia[i] = byteString.charCodeAt(i);
    }

    // write the ArrayBuffer to a blob, and you're done
    return new Blob([ab],{type: mimeString});
}
4
topkara

これは、ES6版の Stoive's answer :です。

export class ImageDataConverter {
  constructor(dataURI) {
    this.dataURI = dataURI;
  }

  getByteString() {
    let byteString;
    if (this.dataURI.split(',')[0].indexOf('base64') >= 0) {
      byteString = atob(this.dataURI.split(',')[1]);
    } else {
      byteString = decodeURI(this.dataURI.split(',')[1]);
    }
    return byteString;
  }

  getMimeString() {
    return this.dataURI.split(',')[0].split(':')[1].split(';')[0];
  }

  convertToTypedArray() {
    let byteString = this.getByteString();
    let ia = new Uint8Array(byteString.length);
    for (let i = 0; i < byteString.length; i++) {
      ia[i] = byteString.charCodeAt(i);
    }
    return ia;
  }

  dataURItoBlob() {
    let mimeString = this.getMimeString();
    let intArray = this.convertToTypedArray();
    return new Blob([intArray], {type: mimeString});
  }
}

使用法:

const dataURL = canvas.toDataURL('image/jpeg', 0.5);
const blob = new ImageDataConverter(dataURL).dataURItoBlob();
let fd = new FormData(document.forms[0]);
fd.append("canvasImage", blob);
3
vekerdyb

ありがとうございます。この解決策については@steovi。

ES6バージョンへのサポートを追加し、unescapeからdataURIに変更しました(unescapeは廃止予定です)。

converterDataURItoBlob(dataURI) {
    let byteString;
    let mimeString;
    let ia;

    if (dataURI.split(',')[0].indexOf('base64') >= 0) {
      byteString = atob(dataURI.split(',')[1]);
    } else {
      byteString = encodeURI(dataURI.split(',')[1]);
    }
    // separate out the mime component
    mimeString = dataURI.split(',')[0].split(':')[1].split(';')[0];

    // write the bytes of the string to a typed array
    ia = new Uint8Array(byteString.length);
    for (var i = 0; i < byteString.length; i++) {
      ia[i] = byteString.charCodeAt(i);
    }
    return new Blob([ia], {type:mimeString});
}
2
wilfredonoyola

簡単にする:D

function dataURItoBlob(dataURI,mime) {
    // convert base64 to raw binary data held in a string
    // doesn't handle URLEncoded DataURIs

    var byteString = window.atob(dataURI);

    // separate out the mime component


    // write the bytes of the string to an ArrayBuffer
    //var ab = new ArrayBuffer(byteString.length);
    var ia = new Uint8Array(byteString.length);
    for (var i = 0; i < byteString.length; i++) {
        ia[i] = byteString.charCodeAt(i);
    }

    // write the ArrayBuffer to a blob, and you're done
    var blob = new Blob([ia], { type: mime });

    return blob;
}
1
Sendy