데이터 URI를 파일로 변환 한 다음 FormData에 추가


283

Mozilla Hacks 사이트 것과 같은 HTML5 이미지 업 로더를 다시 구현하려고 시도 했지만 WebKit 브라우저에서 작동합니다. 작업의 일부는 canvas객체 에서 이미지 파일을 추출하여 업로드 할 FormData 객체에 추가하는 것 입니다.

문제는 동안이다 canvastoDataURL이미지 파일의 표현을 반환하는 기능의 FormData 객체는 파일을 수락하거나 물방울이에서 오브젝트 파일 API .

Mozilla 솔루션은 다음에서 Firefox 전용 기능을 사용했습니다 canvas.

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

... WebKit 브라우저에서는 사용할 수 없습니다. 내가 생각할 수있는 가장 좋은 해결책은 Data URI를 File 객체로 변환 할 수있는 방법을 찾는 것입니다.

가능합니까? 그렇지 않다면 대안이 있습니까?

감사.


서버에 이미지의 DataURI를 저장하려면 : stackoverflow.com/a/50131281/5466401
Sibin John Mattappallil

답변:


471

몇 가지를 가지고 놀다가 스스로 알아낼 수있었습니다.

우선, 이것은 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);

29
왜 이런 일이 항상 발생합니까? 여기저기서 SO 검색으로 몇 시간 동안 문제를 해결하려고합니다. 그런 다음 질문을 게시하십시오. 한 시간 안에 다른 질문에서 답을 얻습니다. 내가 불평하는 것은 아닙니다 ... stackoverflow.com/questions/9388412/…
syaz

나는 물방울 contruct 수 있어요 그러나 당신은 당신이 구성 어떻게 설명해주십시오 수 있습니다 @stoive POST또는 PUTS3에?
Gaurav Shah

1
@mimo-기본을 가리키고 인스턴스에 ArrayBuffer기록됩니다 BlobBuilder.
Stoive

2
@stoive 그렇다면 bb.append (ia)가 아닌 이유는 무엇입니까?
Mimo

4
감사! 이것은 작은 수정으로 내 문제를 해결했습니다var file = new File( [blob], 'canvasImage.jpg', { type: 'image/jpeg' } ); fd.append("canvasImage", file);
Prasad19sara

141

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

2
그냥 생각 : array=[]; array.length=binary.length;... array[i]=bina... 등. 그래서 배열은 미리 할당됩니다. 각 반복마다 배열을 확장 해야하는 push ()를 저장하고 여기서 수백만 개의 항목 (= 바이트)을 처리하고 있으므로 중요합니다.
DDS

2
Safari에서도 실패합니다. @WilliamT. 의 답변은 Firefox / Safari / Chrome에서 작동합니다.
ObscureRobot 2018

"바이너리"는 비트 배열이 아니라 바이트 배열이므로 약간 잘못된 이름입니다.
Niels Abildgaard

1
"type : 'image / jpeg'"-PNG 이미지이거나 이미지 확장명을 미리 모른다면 어떻게해야합니까?
재스퍼

처음에는 Uint8Array 만들기가 배열을 만든 다음 Uint8Array로 변환하는 것보다 낫습니다.
cuixiping

52

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

11
큰! 그러나 Stoive의 솔루션과 같이 여전히 mime 문자열을 동적으로 유지할 수 있다고 생각합니다. // MIME 구성 요소를 분리합니다. var mimeString = dataURI.split ( ',') [0] .split ( ':') [1] .split ( ';') [0]
요청한 Aronsson

웹킷 접두사가있는 iOS6의 대체 기능은 무엇입니까? 이것을 어떻게 처리합니까?
confile

30

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와 함께 사용하여 캔버스 또는 데이터 URL을 처리하십시오.

예를 들면 다음과 같습니다.

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

또한 HTMLCanvasElement.prototype.toBlob비 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);
    };
}

이제 canvas.toBlob()Firefox뿐만 아니라 모든 최신 브라우저에서 작동합니다. 예를 들면 다음과 같습니다.

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

1
여기에 언급 된 canvas.toBlob의 polyfill은이 문제 IMHO를 처리하는 올바른 방법입니다.
Jakob Kruse 2016 년

2
"이제 canvas.toBlob ()은 모든 최신 브라우저에서 작동합니다."
Eric Simonton

25

내가 선호하는 방법은 canvas.toBlob ()입니다.

그러나 어쨌든 여기 fetch를 사용하여 base64를 blob으로 변환하는 또 다른 방법이 있습니다.

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


가져 오기 란 무엇이며 어떻게 관련이 있습니까?
Ricardo Freitas

당신이 대신 사용할 수있는 현대 아약스 방법 가져 오기 XMLHttpRequest해당 자원을 가져 오기 위해 데이터 URL이 단지 URL이, 당신은 아약스를 사용할 수 있기 때문에이 당신 자신에게, 당신이 덩어리로할지 결정 arraybuffer 또는 텍스트 할 수있는 옵션이있어
끝없는

1
@Endless 'fetch ()'로컬 base64 문자열 ... 정말 영리한 핵!
Diego ZoracKy

1
점을 명심 blob:하고 data:보편적으로 모든 가져 오기 구현에 의해 지원되지 않습니다. 우리는 모바일 브라우저 (WebKit) 만 다룰 것이라는 것을 알고 있기 때문에이 방법을 사용 하지만, 예를 들어 Edge는 지원하지 않습니다. developer.mozilla.org/en-US/docs/Web/API/…
oligofren

19

@Stoive 및 @ vava720 덕분에 더 이상 사용되지 않는 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});
}

12

진화하는 표준은 Mozilla가 추측 하기에 canvas.getAsFile ()이 아닌 canvas.toBlob () 인 것으로 보입니다.

아직 지원하는 브라우저가 없습니다. (

이 위대한 스레드에 감사드립니다!

또한 허용되는 답변을 시도하는 사람은 제한적이며 네임 스페이스 인 지원을 찾고 있으므로 BlobBuilder에주의해야합니다.

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

BlobBuilder에 다른 라이브러리의 폴리 필을 사용하고 있습니까?


폴리 필을 사용하지 않고 Chrome을 사용하고 있었으며 이름이 바뀌는 것을 기억하지 않습니다. 나는 간절히 기대한다 canvas.toBlob()-그것보다 훨씬 더 적절한 것 같다 getAsFile.
Stoive

1
BlobBuilder더 이상 사용되지 않는 것 같습니다Blob
sandstrom

BlobBuilder는 더 이상 사용되지 않으며이 패턴은 끔찍합니다. 더 나은 방법은 다음과 같습니다. window.BlobBuilder = (window.BlobBuilder || window.WebKitBlobBuilder || window.MozBlobBuilder || window.MSBlobBuilder); 중첩 된 try catch는 실제로 추악하며 사용 가능한 빌더가 없으면 어떻게됩니까?
Chris Hanson

이것은 어떻게 끔찍합니까? 예외가 발생하고 1) BlobBuilder가 존재하지 않으면 아무 일도 일어나지 않고 다음 블록이 실행됩니다. 2) 존재하지만 예외가 발생하면 더 이상 사용되지 않으며 사용해서는 안되므로 다음 try 블록으로 계속 진행됩니다. 이상적으로는 Blob이 먼저 지원되는지 확인하고 toBlob 지원을 확인하기 전에
TaylorMac

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

try catch없이 사용할 수 있습니다.

check_ca에 감사합니다. 훌륭한 일.


1
브라우저가 더 이상 사용되지 않는 BlobBuilder를 지원하는 경우 여전히 오류가 발생합니다. 브라우저는 새 방법을 지원하더라도 이전 방법을 지원하는 경우이를 사용합니다. 이것은 바람직하지 않습니다. 아래 Chris Bosco의 접근 방식을 참조하십시오
TaylorMac

4

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

3

Stoive의 답변에 대한 ES6 버전은 다음과 같습니다 .

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

감사! 이 솔루션에 대한 @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});
}

1

간단하게 : 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은 문자열을 제공하며 해당 문자열을 숨겨진 입력에 넣을 수 있습니다.


예를 들어 주시겠습니까? 나는 base64 문자열 <input type=hidden value="data:..." />을 업로드하고 싶지 <input type="file" />않다 value.
Stoive

이것은 답변이 아닌 의견이어야합니다. 적절한 설명으로 답을 정교하게 작성하십시오. @ 캣 첸
럭키

-5

나는 Ravinder Payal과 정확히 같은 문제가 있었고 그 답을 찾았습니다. 이 시도:

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

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

2
정말로 Parse.com을 사용하도록 제안하고 있습니까? 대답에 의존성이 필요하다는 것을 언급해야합니다!
Pierre Maoui

2
WTF? base64 코드의 이미지를 PARSE 서버에 업로드 한 다음 다운로드해야하는 이유 서버에 base64를 직접 업로드 할 수 있고 가장 중요한 것은 base64 문자열 또는 이미지 파일을 업로드하는 데 동일한 데이터가 필요하다는 것입니다. 그리고 사용자가 이미지를 다운로드하도록하기를 원한다면 이것을 사용할 수 있습니다window.open(canvas.toDataURL("image/jpeg"))
Ravinder Payal
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.