web-dev-qa-db-fra.com

Convertir l'URI des données en fichier puis ajouter à FormData

J'ai essayé de réimplémenter un téléchargeur d'image HTML5 tel que 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 à l'ajouter à un objet FormData pour le téléchargement.

Le problème est que, bien que canvas ait la fonction toDataURL pour renvoyer une représentation du fichier image, l'objet FormData n'accepte que les objets File ou Blob de l'API File .

La solution Mozilla utilisait la fonction suivante uniquement pour Firefox sur canvas:

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

... qui n'est pas disponible sur les navigateurs WebKit. La meilleure solution à laquelle je puisse penser est de trouver un moyen de convertir un URI de données en un objet File, ce qui, à mon avis, pourrait faire partie de l'API File, mais je ne peux pas, pour la vie, trouver quelque chose à faire.

C'est possible? Si non, des alternatives?

Merci.

253
Stoive

Après avoir joué avec plusieurs choses, j'ai réussi à comprendre cela moi-même.

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

À partir de là, il est facile d’ajouter les données à un formulaire de telle sorte qu’il soit chargé en tant que fichier:

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

BlobBuilder et ArrayBuffer sont maintenant obsolètes, voici le code du commentaire supérieur mis à jour avec le constructeur 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'});
}
138
vava720

Celui-ci fonctionne dans iOS et Safari.

Vous devez utiliser la solution ArrayBuffer de Stoive, mais vous ne pouvez pas utiliser BlobBuilder, comme l'indique vava720. Voici donc 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' });
}
51
William T.

Firefox a les méthodes canvas.toBlob () et canvas.mozGetAsFile ()

Mais les autres navigateurs ne le font pas. 

Nous pouvons obtenir dataurl à partir de canvas puis convertir dataurl 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 gérer votre canevas 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");

En outre, vous pouvez créer une méthode HTMLCanvasElement.prototype.toBlob pour un navigateur 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, canvas.toBlob() fonctionne pour tous les navigateurs modernes, pas 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
);
24
cuixiping

Ma manière préférée est canvas.toBlob ()

Mais de toute façon, voici encore un autre moyen 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})
})

19
Endless

Grâce à @Stoive et @ vava720, j'ai combiné les deux de cette manière, en évitant d'utiliser les obsolètes 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
Mimo

La norme en évolution semble être canvas.toBlob () not canvas.getAsFile () comme Mozilla a risqué de le deviner.

Je ne vois pas encore de navigateur le supporter :(

Merci pour ce super fil!

En outre, toute personne essayant la réponse acceptée doit faire attention à BlobBuilder car je trouve que le support est limité (et que les noms sont espacés):

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

Utilisiez-vous le polyfill d'une autre bibliothèque pour BlobBuilder?

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

peut être utilisé sans la prise d'essai. 

Merci à check_ca. Bon travail. 

5
Nafis Ahmad

La réponse originale de Stoive peut être facilement résolue en changeant la dernière ligne pour tenir compte de 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

Voici une version ES6 de Stoive répond :

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

Usage: 

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

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
Sendy

Merci! @steovi pour cette solution.

J'ai ajouté le support à la version ES6 et je suis passé de unescape à dataURI (unescape est obsolète).

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
wilfredonoyola