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


283

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

문제는 동안이다 canvas이 toDataURL이미지 파일의 표현을 반환하는 기능의 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.