Convertir URI de données en Fichier, puis Ajouter à FormData

j'ai essayé de ré-implémenter un téléchargement d'image HTML5 comme celui sur le site Mozilla Hacks , mais cela fonctionne avec les navigateurs WebKit. Une partie de la tâche consiste à extraire un fichier image de l'objet canvas et de l'ajouter à un FormData objet à télécharger.

le problème est que tandis que canvas a la fonction toDataURL pour retourner une représentation du fichier image, L'objet FormData accepte seulement Les objets File ou Blob de "File API .

la solution Mozilla utilisait la fonction Firefox suivante sur canvas :

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

...qui n'est pas disponible sur les navigateurs WebKit. La meilleure solution que je puisse imaginer est de trouver un moyen de convertir un URI de données en un objet de fichier, qui je pensais pourrait faire partie de L'API de fichier, mais je ne peux pas pour la vie de moi trouver quelque chose à faire.

est-il possible? Si non, des alternatives?

Merci.

251
demandé sur Stoive 2011-02-15 03:40:44

14 réponses

après avoir joué avec quelques trucs, j'ai réussi à le découvrir moi-même.

tout D'abord, cela convertira un dataURI en 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});
}

de là, ajouter les données à un formulaire tel qu'il sera téléchargé comme un fichier est facile:

var dataURL = canvas.toDataURL('image/jpeg', 0.5);
var blob = dataURItoBlob(dataURL);
var fd = new FormData(document.forms[0]);
fd.append("canvasImage", blob);
417
répondu Stoive 2014-10-20 23:04:24

BlobBuilder et ArrayBuffer sont maintenant dépréciés, voici le code du Commentaire du Haut mis à jour avec Blob constructor:

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'});
}
138
répondu vava720 2012-08-14 14:19:55

celui-ci travaille à iOS et Safari.

vous devez utiliser la solution ArrayBuffer de Stoive mais vous ne pouvez pas utiliser BlobBuilder, comme vava720 indique, donc voici le mashup des deux.

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' });
}
50
répondu William T. 2013-04-02 00:18:53

Firefox a de la toile.toBlob () et canvas.méthodes mozGetAsFile ().

mais pas les autres navigateurs.

on peut obtenir des données à partir de canvas et ensuite convertir des données en objet blob.

voici ma fonction dataURLtoBlob (). C'est très court.

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

utilisez cette fonction avec FormData pour manipuler votre toile ou votre dataurl.

par exemple:

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

vous pouvez aussi créer un HTMLCanvasElement.prototype.méthode toBlob pour navigateur moteur non gecko.

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

maintenant toile.toBlob () fonctionne pour tous les navigateurs modernes non seulement firefox. par exemple:

canvas.toBlob(
    function(blob){
        var fd = new FormData();
        fd.append("myFile", blob, "thumb.jpg");
        //continue do something...
    },
    'image/jpeg',
    0.8
);
22
répondu cuixiping 2015-05-26 22:51:06

grâce à @Stoive et @vava720 j'ai combiné les deux de cette façon, en évitant d'utiliser l'obsolète BlobBuilder et 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
répondu Mimo 2013-02-18 06:33:02

Ma voie préférée est toile.toBlob ()

mais en tout cas voici une autre façon de convertir base64 en blob en utilisant fetch ^^,

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})
})
16
répondu Endless 2017-09-01 18:48:47

le standard évolutif semble être toile.toBlob() pas de toile.getAsFile() comme Mozilla exposé à deviner.

Je ne vois pas encore de navigateur le supportant: (

Merci pour ce grand fil!

aussi, quiconque essaie la réponse acceptée devrait faire attention avec BlobBuilder car je trouve le support pour être limité (et namespaced):

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

utilisiez-vous un autre le polyfill de la bibliothèque pour BlobBuilder?

12
répondu Chris Bosco 2011-06-27 21:36:26
var BlobBuilder = (window.MozBlobBuilder || window.WebKitBlobBuilder || window.BlobBuilder);

peut être utilisé sans le try catch.

merci à check_ca. Un excellent travail.

5
répondu Nafis Ahmad 2012-01-29 16:32:01

la réponse originale de Stoive est facilement fixable en changeant la dernière ligne pour accommoder 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
répondu topkara 2013-07-16 17:14:09

Voici une version ES6 de réponse de Stoive :

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

Utilisation:

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
répondu vekerdyb 2017-05-23 12:26:10

faire simple :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
répondu Sendy 2014-09-07 19:16:19

Merci! @steovi pour cette solution.

j'ai ajouté le support à la version ES6 et j'ai changé de unescape à dataURI(unescape est déprécié).

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});
}
0
répondu wilfredonoyola 2018-04-09 16:12:56

toDataURL vous donne une chaîne et vous pouvez mettre cette chaîne à une entrée cachée.

-1
répondu Cat Chen 2011-02-23 14:19:44

j'ai eu exactement le même problème que Ravinder Payal, et j'ai trouvé la réponse. Essayez ceci:

var dataURL = canvas.toDataURL("image/jpeg");

var name = "image.jpg";
var parseFile = new Parse.File(name, {base64: dataURL.substring(23)});
-4
répondu feng 2014-08-30 17:28:46