Veri URI'sini Dosyaya Dönüştür, sonra FormData'ya ekle


283

Mozilla Hacks sitesindeki gibi bir HTML5 resim yükleyiciyi yeniden uygulamaya çalışıyorum , ancak WebKit tarayıcılarıyla çalışıyor. Görevin bir kısmı, bir görüntü dosyasını canvasnesneden ayıklamak ve yüklemek için bir FormData nesnesine eklemektir .

Sorun, görüntü dosyasının bir temsilini döndürme işlevine sahip olsa canvasda, toDataURLFormData nesnesi yalnızca Dosya API'sından File veya Blob nesnelerini kabul eder .

Mozilla çözümü aşağıdaki Firefox'a özel aşağıdaki işlevi kullanmıştır canvas:

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

... WebKit tarayıcılarında mevcut değil. Düşünebileceğim en iyi çözüm, Veri API'sini Dosya API'sine dönüştürmenin bir yolunu bulmaktır, ki bu da Dosya API'sinin bir parçası olabileceğini düşündüm, ancak hayatım boyunca bunu yapacak bir şey bulamıyorum.

Mümkün mü? Değilse, başka alternatifler var mı?

Teşekkürler.


Bir görüntünün DataURI'sini sunucuya kaydetmek istiyorsanız: stackoverflow.com/a/50131281/5466401
Sibin John Mattappallil

Yanıtlar:


471

Birkaç şeyle oynadıktan sonra bunu kendim çözmeyi başardım.

Her şeyden önce, bu bir dataURI'yi bir Blob'a dönüştürür:

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

Oradan, verileri bir dosyaya yüklenecek şekilde bir forma eklemek kolaydır:

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

29
Bu her zaman neden oluyor ... Burada ve orada SO aramalarıyla saatlerce saatlerce bir sorunu çözmeye çalışıyorsunuz. Sonra bir soru gönderirsiniz. Bir saat içinde başka bir sorunun cevabını alırsınız. Şikayet ettiğimden değil ... stackoverflow.com/questions/9388412/…
syaz

Ben Blob contruct mümkün ama sen inşa nasıl açıklar mısınız olabilir @stoive POSTveya PUTS3?
Gaurav Shah

1
@mimo - Altta yatan yeri gösterir ve ArrayBufferdaha sonra BlobBuilderörneğe yazılır .
Stoive

2
@stoive Bu durumda neden bb.append (ia) değil?
Mimo

4
Teşekkürler! Bu, küçük bir düzeltme ile sorunumu çözdüvar file = new File( [blob], 'canvasImage.jpg', { type: 'image/jpeg' } ); fd.append("canvasImage", file);
Prasad19sara

141

BlobBuilder ve ArrayBuffer artık kullanımdan kaldırıldı, en iyi yorumun Blob yapıcısı ile güncellenen kodu:

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

2
Sadece bir fikir: array=[]; array.length=binary.length;... array[i]=bina... vs. Yani dizi önceden ayrılmıştır. Diziyi her yinelemeyi genişletmek zorunda olan bir push () kaydeder ve burada muhtemelen milyonlarca öğeyi (= bayt) işliyoruz, bu yüzden önemlidir.
DDS

2
Safari'de de benim için başarısız. @WilliamT. yanıtı Firefox / Safari / Chrome için de geçerli.
ObscureRobot

"ikili" bir bit dizisi değil, bir bayt dizisi olduğu için biraz yanıltıcı bir isimdir.
Niels Abildgaard

1
"type: 'image / jpeg'" - PNG resmi ise VEYA resim uzantısını önceden bilmiyorsanız ne olur?
Jasper

İlk önce Uint8Array oluşturmak bir Array oluşturmak ve daha sonra Uint8Array'e dönüştürmekten daha iyidir.
cuixiping

52

Bu iOS ve Safari'de çalışıyor.

Stoive'ın ArrayBuffer çözümünü kullanmanız gerekiyor, ancak vava720'nin belirttiği gibi BlobBuilder'ı kullanamazsınız, bu yüzden her ikisinin karışımı.

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

11
Harika! Ama yine de MIME dizesini dinamik tutabilirsiniz, sanırım Stoive'ın çözümünde olduğu gibi? // mime bileşenini ayırmak var mimeString = dataURI.split (',') [0] .split (':') [1] .split (';') [0]
Görev Başına Vuruş Başına

Webkit önekine sahip iOS6'nın yedeği nedir? Bununla nasıl başa çıkıyorsun?
2014'te

30

Firefox'un canvas.toBlob () ve canvas.mozGetAsFile () yöntemleri vardır.

Ancak diğer tarayıcılar bunu yapmaz.

Tuvalden dataurl alabilir ve daha sonra dataurl'u blob nesnesine dönüştürebiliriz.

İşte benim dataURLtoBlob()fonksiyonum. Çok kısa.

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

Tuvalinizi veya dataurl'nizi işlemek için FormData ile bu işlevi kullanın.

Örneğin:

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

Ayrıca, HTMLCanvasElement.prototype.toBlobgecko olmayan motor tarayıcı için bir yöntem oluşturabilirsiniz .

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

Artık canvas.toBlob()sadece Firefox için değil, tüm modern tarayıcılar için çalışıyor. Örneğin:

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

1
Canvas.toBlob için burada belirtilen çoklu dolgu, bu sorunu IMHO ile başa çıkmanın doğru yoludur.
Jakob Kruse

2
Bu yayındaki son şeyi vurgulamak istiyorum: "Şimdi canvas.toBlob () tüm modern tarayıcılar için çalışıyor."
Eric Simonton

25

Tercih ettiğim yol canvas.toBlob ()

Ama yine de burada fet64 kullanarak base64 bir blob dönüştürmek için başka bir yoludur,

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


getirme nedir ve nasıl alakalı?
Ricardo Freitas

Getirme, XMLHttpRequestveri URL'si yalnızca bir URL olduğu için kullanabileceğiniz modern bir ajax yöntemidir , ajax'ı bu kaynağı getirmek için kullanabilirsiniz ve kendinize bunu blob, arraybuffer veya metin olarak isteyip istemediğinize karar verme seçeneğiniz vardır
Endless

1
@ Endless 'fetch ()' yerel bir base64 dizesi ... gerçekten akıllı bir kesmek!
Diego ZoracKy

1
Unutmayın blob:ve data:tüm getirme uygulamaları tarafından evrensel olarak desteklenmemektedir. Bu yaklaşımı kullanıyoruz, çünkü yalnızca mobil tarayıcılarla (WebKit) ilgileneceğimizi biliyoruz , ancak Edge örneğin bunu desteklemiyor: developer.mozilla.org/en-US/docs/Web/API/…
oligofren

19

@Stoive ve @ vava720 sayesinde, kullanımdan kaldırılmış BlobBuilder ve ArrayBuffer'ı kullanmaktan kaçınarak ikisini bu şekilde birleştirdim.

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

12

Gelişen standart Mozilla'nın tahmin etmesi tehlikeye düştüğü için canvas.getBlob () değil canvas.getAsFile () gibi görünüyor.

Henüz destekleyen bir tarayıcı göremiyorum :(

Bu harika konu için teşekkürler!

Ayrıca, sınırlı yanıtı (ve ad boşluklu) destek bulduğum için, kabul edilen yanıtı deneyen herkes BlobBuilder'e dikkat etmelidir:

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

BlobBuilder için başka bir kütüphanenin çoklu dolgusunu mu kullanıyordunuz?


Çoklu dolgular olmadan Chrome kullanıyordum ve ad boşluğuyla karşılaştığımı hatırlamıyorum. Merakla bekliyorum canvas.toBlob()- bundan çok daha uygun görünüyor getAsFile.
Stoive

1
BlobBuilderlehine Blob
itiraz

BlobBuilder kullanımdan kaldırıldı ve bu model korkunç. Daha iyisi: window.BlobBuilder = (window.BlobBuilder || window.WebKitBlobBuilder || window.MozBlobBuilder || window.MSBlobBuilder); yuvalanmış deneme yakalamaları gerçekten çirkin ve inşaatçıların hiçbiri yoksa ne olur?
Chris Hanson

Bu nasıl korkunç? Bir istisna atılırsa ve 1) BlobBuilder yoksa, hiçbir şey olmaz ve sonraki blok yürütülür. 2) Varsa, ancak bir istisna atılırsa, kullanımdan kaldırılır ve yine de kullanılmamalıdır, bu nedenle bir sonraki deneme bloğuna devam eder. İdeal olarak, Blob'un önce desteklenip desteklenmediğini ve hatta toBlob desteği için bu kontrolden önce kontrol edersiniz
TaylorMac

5
var BlobBuilder = (window.MozBlobBuilder || window.WebKitBlobBuilder || window.BlobBuilder);

try catch olmadan kullanılabilir.

Check_ca'ya teşekkürler. Harika iş.


1
Tarayıcı kullanımdan kaldırılmış BlobBuilder'ı destekliyorsa, bu yine de bir hata verir. Tarayıcı, eski yöntemi destekliyorsa, yeni yöntemi desteklese bile kullanır. Bu istenmiyor, aşağıdaki Chris Bosco'nun yaklaşımına bakınız
TaylorMac

4

Stoive'ın orijinal cevabı, Blob'a uygun son satırı değiştirerek kolayca düzeltilebilir:

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

3

İşte Stoive'ın cevabının ES6 sürümü :

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

Kullanımı:

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

Teşekkürler! @steovi bu çözüm için.

ES6 sürümüne destek ekledim ve unescape'ten dataURI'ye geçtim (unescape kullanımdan kaldırıldı).

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

1

basitleştirin: 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;
}

-2

toDataURL size bir dize verir ve bu dizeyi gizli bir girişe koyabilirsiniz.


Lütfen bir örnek verebilir misiniz? Bir base64 dize yüklemek istemiyorum (bu ne yapıyor <input type=hidden value="data:..." />olur), ben dosya verilerini yüklemek istiyorum (ne <input type="file" />gibi, bu valueözelliği ayarlamak için izin verilmez dışında ).
Stoive

Bu bir cevaptan ziyade bir yorum olmalıdır. Lütfen cevabı uygun bir açıklama ile hazırlayın. @Cat Chen
Şanslı

-5

Ravinder Payal ile tamamen aynı problemi yaşadım ve cevabı buldum. Bunu dene:

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

var name = "image.jpg";
var parseFile = new Parse.File(name, {base64: dataURL.substring(23)});

2
Gerçekten Parse.com'u kullanmanızı öneriyor musunuz? Cevabınızın bağımlılıklar gerektirdiğini belirtmelisiniz!
Pierre Maoui

2
O NE LAN ? Neden herhangi biri base64 görüntü kodunu PARSE sunucusuna yükler ve sonra indirir? Ve sadece görüntüyü indirmek için kullanıcı alow istiyorsanız, bunu kullanabilirsinizwindow.open(canvas.toDataURL("image/jpeg"))
Ravinder Payal
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.