JavaScript로 Base64 문자열에서 BLOB 만들기


447

문자열에 Base64로 인코딩 된 이진 데이터가 있습니다.

const contentType = 'image/png';
const b64Data = 'iVBORw0KGgoAAAANSUhEUgAAAAUAAAAFCAYAAACNbyblAAAAHElEQVQI12P4//8/w38GIAXDIBKE0DHxgljNBAAO9TXL0Y4OHwAAAABJRU5ErkJggg==';

blob:이 데이터가 포함 된 URL 을 만들어 사용자에게 표시하고 싶습니다 .

const blob = new Blob(????, {type: contentType});
const blobUrl = URL.createObjectURL(blob);

window.location = blobUrl;

BLOB을 만드는 방법을 알 수 없었습니다.

어떤 경우에는 data:대신 URL 을 사용하여 이것을 피할 수 있습니다 .

const dataUrl = `data:${contentType};base64,${b64Data}`;

window.location = dataUrl;

그러나 대부분의 경우 data:URL이 엄청나게 큽니다.


JavaScript에서 Base64 문자열을 BLOB 객체로 어떻게 디코딩합니까?

답변:


790

atob함수는 Base64로 인코딩 된 문자열을 이진 데이터의 각 바이트에 대한 문자가 포함 된 새 문자열로 디코딩합니다.

const byteCharacters = atob(b64Data);

각 문자의 코드 포인트 (charCode)는 바이트 값입니다. .charCodeAt문자열의 각 문자에 대한 방법을 사용하여 이것을 적용하여 바이트 값의 배열을 만들 수 있습니다 .

const byteNumbers = new Array(byteCharacters.length);
for (let i = 0; i < byteCharacters.length; i++) {
    byteNumbers[i] = byteCharacters.charCodeAt(i);
}

이 바이트 값 배열을 Uint8Array생성자 에 전달하여 실제 형식의 바이트 배열로 변환 할 수 있습니다 .

const byteArray = new Uint8Array(byteNumbers);

이 배열을 배열로 감싸서 Blob생성자에 전달하여 BLOB로 변환 할 수 있습니다 .

const blob = new Blob([byteArray], {type: contentType});

위의 코드가 작동합니다. 그러나 byteCharacters한 번에가 아니라 더 작은 슬라이스 로 처리하여 성능을 약간 향상시킬 수 있습니다 . 거친 테스트에서 512 바이트는 좋은 슬라이스 크기 인 것 같습니다. 이것은 우리에게 다음과 같은 기능을 제공합니다.

const b64toBlob = (b64Data, contentType='', sliceSize=512) => {
  const byteCharacters = atob(b64Data);
  const byteArrays = [];

  for (let offset = 0; offset < byteCharacters.length; offset += sliceSize) {
    const slice = byteCharacters.slice(offset, offset + sliceSize);

    const byteNumbers = new Array(slice.length);
    for (let i = 0; i < slice.length; i++) {
      byteNumbers[i] = slice.charCodeAt(i);
    }

    const byteArray = new Uint8Array(byteNumbers);
    byteArrays.push(byteArray);
  }

  const blob = new Blob(byteArrays, {type: contentType});
  return blob;
}
const blob = b64toBlob(b64Data, contentType);
const blobUrl = URL.createObjectURL(blob);

window.location = blobUrl;

전체 예 :


6
제레미 안녕하세요. 우리는 웹 응용 프로그램 에서이 코드를 가지고 있으며 다운로드되는 파일의 크기가 더 커질 때까지 아무런 문제가 발생하지 않았습니다. 따라서 사용자가 Chrome 또는 IE를 사용하여 100MB보다 큰 파일을 다운로드 할 때 프로덕션 서버에서 중단 및 충돌이 발생했습니다. IE에서 다음 줄은 메모리 예외 "var byteNumbers = new Array (slice.length)"를 발생시키는 것으로 나타났습니다. 그러나 크롬에서는 동일한 문제를 일으키는 for 루프였습니다. 우리는이 문제에 대한 적절한 해결책을 찾지 못했고 window.open을 사용하여 파일을 직접 다운로드하는 것으로 옮겼습니다. 여기에 도움을 줄 수 있습니까?
Akshay Raut

반응 네이티브에서 비디오 파일을 base64로 변환하는 방법이 있습니까? 나는 이미지 파일로 그렇게했지만 비디오에 대한 해결책을 찾지 못했습니다. 링크도 도움이 될 것입니다.
Diksha235

따라서 atob ()에 의해 반환 된 문자열에 0을 저장하는 데 문제가 있습니까?
wcochran

이것은 Chrome 및 Firefox의 일부 얼룩에서는 작동하지 않지만 가장자리에서 작동했습니다
./

나를 위해 일했다. ** JSON 구문 분석 오류가 발생했습니다 : 인식 할 수없는 toke '<'** 이미지를 만들고있는 브라우저에 base64 문자열을 확인했습니다. 도움이 필요해.
Aman Deep

273

다음은 종속성이나 라이브러리가없는보다 최소한의 방법입니다.
새로운 가져 오기 API가 필요합니다. ( 사용할 수 있습니까? )

var url = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAUAAAAFCAYAAACNbyblAAAAHElEQVQI12P4//8/w38GIAXDIBKE0DHxgljNBAAO9TXL0Y4OHwAAAABJRU5ErkJggg=="

fetch(url)
.then(res => res.blob())
.then(console.log)

이 방법을 사용하면 ReadableStream, ArrayBuffer, text 및 JSON도 쉽게 얻을 수 있습니다.

기능으로서 :

const b64toBlob = (base64, type = 'application/octet-stream') => 
  fetch(`data:${type};base64,${base64}`).then(res => res.blob())

Jeremy의 ES6 동기화 버전에 대한 간단한 성능 테스트를 수행했습니다.
동기화 버전은 UI를 잠시 차단합니다. devtool을 열린 상태로 유지하면 가져 오기 성능이 느려질 수 있습니다


1
base64로 인코딩 된 문자열의 크기가 큰 경우에도 여전히 작동합니까? Opera에서 URI 크기의 제한 인 665536자를 초과한다고 가정하겠습니다.
Daniel Kats

1
몰라, 그것이 주소 표시 줄에 제한이 될 수 있다는 것을 알고 있지만 AJAX로 작업하는 것은 렌더링 할 필요가 없기 때문에 예외가 될 수 있습니다. 테스트해야합니다. 그것이 내가 어디에 있으면 처음에 base64 문자열을 얻지 못했을 것입니다. 나쁜 습관이라고 생각하면 디코딩 및 인코딩하는 데 더 많은 메모리와 시간이 필요합니다. createObjectURL대신에 readAsDataURL훨씬 더 좋습니다. 그리고 ajax를 사용하여 파일을 업로드하는 경우 FormData대신 대신을 선택 JSON하거나 canvas.toBlob대신 사용하십시오toDataURL
Endless

7
인라인으로 더 나은 :await (await fetch(imageDataURL)).blob()
icl7126

3
최신 브라우저를 타겟팅하는 경우 확실합니다. 그러나이를 위해서는 함수가 비동기 함수 안에 있어야합니다. 말하자면 ... await fetch(url).then(r=>r.blob())분류기
끝없는

2
매우 깔끔한 솔루션이지만 내 지식에 따르면 Access is denied.오류 로 인해 IE (polyfill ofc 포함)에서는 작동하지 않습니다 . 나는 fetch같은 방식으로 BLOB URL 아래에 BLOB을 노출 URL.createObjectUrl한다고 생각합니다 .ie11에서는 작동하지 않습니다. 참조 . IE11에서 가져 오기를 사용하는 방법이 있습니까? 다른 동기화 솔루션보다 훨씬 나아 보입니다 :)
Papi

72

최적화 된 (하지만 읽기 어려운) 구현 :

function base64toBlob(base64Data, contentType) {
    contentType = contentType || '';
    var sliceSize = 1024;
    var byteCharacters = atob(base64Data);
    var bytesLength = byteCharacters.length;
    var slicesCount = Math.ceil(bytesLength / sliceSize);
    var byteArrays = new Array(slicesCount);

    for (var sliceIndex = 0; sliceIndex < slicesCount; ++sliceIndex) {
        var begin = sliceIndex * sliceSize;
        var end = Math.min(begin + sliceSize, bytesLength);

        var bytes = new Array(end - begin);
        for (var offset = begin, i = 0; offset < end; ++i, ++offset) {
            bytes[i] = byteCharacters[offset].charCodeAt(0);
        }
        byteArrays[sliceIndex] = new Uint8Array(bytes);
    }
    return new Blob(byteArrays, { type: contentType });
}

2
바이트를 블롭으로 분할하는 이유가 있습니까? 사용하지 않으면 단점이나 위험이 있습니까?
Alfred Huang

Ionic 1 / Angular 1을 사용하여 Android에서 훌륭하게 작동합니다. 슬라이스가 필요합니다. 그렇지 않으면 OOM (Android 6.0.1)에 빠집니다.
Jürgen 'Kashban'Wahlmann

4
IE 11과 Chrome의 엔터프라이즈 환경에서 모든 문서 유형으로 완벽하게 작업 할 수있는 예가 있습니다.
santos

이건 끝내줘. 감사합니다!
elliotwesoff

순서대로 설명하겠습니다. 예를 들어 왜 성능이 더 높은가요?
Peter Mortensen

19

모든 브라우저 지원, 특히 Android에서 다음을 추가 할 수 있습니다.

try{
    blob = new Blob(byteArrays, {type : contentType});
}
catch(e){
    // TypeError old Google Chrome and Firefox
    window.BlobBuilder = window.BlobBuilder ||
                         window.WebKitBlobBuilder ||
                         window.MozBlobBuilder ||
                         window.MSBlobBuilder;
    if(e.name == 'TypeError' && window.BlobBuilder){
        var bb = new BlobBuilder();
        bb.append(byteArrays);
        blob = bb.getBlob(contentType);
    }
    else if(e.name == "InvalidStateError"){
        // InvalidStateError (tested on FF13 WinXP)
        blob = new Blob(byteArrays, {type : contentType});
    }
    else{
        // We're screwed, blob constructor unsupported entirely
    }
}

감사합니다. 그러나 올바르게 읽으면 위에서 작성한 코드 스 니펫에 두 가지 문제가 있습니다. new Blob (byteArrays, {type : contentType}) "... 왜 원래 예외 후에 동일한 코드를 반복하도록 제안합니까? ... (2) BlobBuilder.append ()는 바이트 배열을 허용하지 않고 ArrayBuffer를 허용합니다. 따라서이 API를 사용하기 전에 입력 바이트 배열을 ArrayBuffer로 변환해야합니다. REF : developer.mozilla.org/en-US/docs/Web/API/BlobBuilder
Panini Luncher

14

이미지 데이터의 경우 사용이 더 간단합니다 canvas.toBlob(비동기식)

function b64toBlob(b64, onsuccess, onerror) {
    var img = new Image();

    img.onerror = onerror;

    img.onload = function onload() {
        var canvas = document.createElement('canvas');
        canvas.width = img.width;
        canvas.height = img.height;

        var ctx = canvas.getContext('2d');
        ctx.drawImage(img, 0, 0, canvas.width, canvas.height);

        canvas.toBlob(onsuccess);
    };

    img.src = b64;
}

var base64Data = 'data:image/jpg;base64,/9j/4AAQSkZJRgABAQA...';
b64toBlob(base64Data,
    function(blob) {
        var url = window.URL.createObjectURL(blob);
        // do something with url
    }, function(error) {
        // handle error
    });

1
메타 정보처럼 이미지를 png로 변환하는 것과 같은 정보
Endless

image/jpgbase64 문자열에서 이미지 유형 을 추출 toBlob하고 결과가 동일한 유형이되도록 함수에 두 번째 매개 변수로 전달하여 이미지를 개선 할 수 있다고 생각합니다 . 그 외에는 이것이 완벽하다고 생각합니다-트래픽의 30 %와 서버의 디스크 공간 (base64와 비교)을 30 % 절약하고 투명한 PNG로도 잘 작동합니다.
icl7126

1
이 기능은 2MB보다 큰 이미지와 충돌합니다 ... Android에서 예외가 발생합니다 : android.os.TransactionTooLarge
Ruben

14

이 예를 참조하십시오 : https://jsfiddle.net/pqhdce2L/

function b64toBlob(b64Data, contentType, sliceSize) {
  contentType = contentType || '';
  sliceSize = sliceSize || 512;

  var byteCharacters = atob(b64Data);
  var byteArrays = [];

  for (var offset = 0; offset < byteCharacters.length; offset += sliceSize) {
    var slice = byteCharacters.slice(offset, offset + sliceSize);

    var byteNumbers = new Array(slice.length);
    for (var i = 0; i < slice.length; i++) {
      byteNumbers[i] = slice.charCodeAt(i);
    }

    var byteArray = new Uint8Array(byteNumbers);

    byteArrays.push(byteArray);
  }
    
  var blob = new Blob(byteArrays, {type: contentType});
  return blob;
}


var contentType = 'image/png';
var b64Data = Your Base64 encode;

var blob = b64toBlob(b64Data, contentType);
var blobUrl = URL.createObjectURL(blob);

var img = document.createElement('img');
img.src = blobUrl;
document.body.appendChild(img);


순서대로 설명하겠습니다.
Peter Mortensen

9

Jeremy가 제안한대로 데이터를자를 때 Internet Explorer 11이 매우 느려집니다. 이것은 Chrome의 경우에 해당하지만 Internet Explorer는 슬라이스 된 데이터를 Blob-Constructor로 전달할 때 문제가있는 것 같습니다. 내 컴퓨터에서 5MB의 데이터를 전달하면 Internet Explorer가 중단되고 메모리 소비가 급증합니다. Chrome은 신속하게 얼룩을 만듭니다.

비교를 위해이 코드를 실행하십시오.

var byteArrays = [],
    megaBytes = 2,
    byteArray = new Uint8Array(megaBytes*1024*1024),
    block,
    blobSlowOnIE, blobFastOnIE,
    i;

for (i = 0; i < (megaBytes*1024); i++) {
    block = new Uint8Array(1024);
    byteArrays.push(block);
}

//debugger;

console.profile("No Slices");
blobSlowOnIE = new Blob(byteArrays, { type: 'text/plain'});
console.profileEnd();

console.profile("Slices");
blobFastOnIE = new Blob([byteArray], { type: 'text/plain'});
console.profileEnd();

그래서 Jeremy가 설명한 두 가지 방법을 하나의 함수에 포함하기로 결정했습니다. 크레딧은 그를 위해 간다.

function base64toBlob(base64Data, contentType, sliceSize) {

    var byteCharacters,
        byteArray,
        byteNumbers,
        blobData,
        blob;

    contentType = contentType || '';

    byteCharacters = atob(base64Data);

    // Get BLOB data sliced or not
    blobData = sliceSize ? getBlobDataSliced() : getBlobDataAtOnce();

    blob = new Blob(blobData, { type: contentType });

    return blob;


    /*
     * Get BLOB data in one slice.
     * => Fast in Internet Explorer on new Blob(...)
     */
    function getBlobDataAtOnce() {
        byteNumbers = new Array(byteCharacters.length);

        for (var i = 0; i < byteCharacters.length; i++) {
            byteNumbers[i] = byteCharacters.charCodeAt(i);
        }

        byteArray = new Uint8Array(byteNumbers);

        return [byteArray];
    }

    /*
     * Get BLOB data in multiple slices.
     * => Slow in Internet Explorer on new Blob(...)
     */
    function getBlobDataSliced() {

        var slice,
            byteArrays = [];

        for (var offset = 0; offset < byteCharacters.length; offset += sliceSize) {
            slice = byteCharacters.slice(offset, offset + sliceSize);

            byteNumbers = new Array(slice.length);

            for (var i = 0; i < slice.length; i++) {
                byteNumbers[i] = slice.charCodeAt(i);
            }

            byteArray = new Uint8Array(byteNumbers);

            // Add slice
            byteArrays.push(byteArray);
        }

        return byteArrays;
    }
}

이것을 포함 해 주셔서 감사합니다. IE11에 대한 최신 업데이트 (2016 년 5 월 ~ 2016 년 8 월 사이)에서 어레이에서 얼룩을 생성하면 훨씬 많은 양의 램이 사용되기 시작했습니다. 단일 Uint8Array를 블로그 생성자로 보내어 거의 램을 사용하지 않고 실제로 프로세스를 완료했습니다.
Andrew Vogel

테스트 샘플에서 슬라이스 크기를 1K에서 8..16K로 늘리면 IE의 시간이 크게 줄어 듭니다. 내 PC에서 원래 코드는 5 초에서 8 초가 걸렸습니다. 8K 블록이있는 코드는 356ms, 16K 블록에 225ms가 걸렸습니다
Victor

6

나와 같은 모든 복사 붙여 넣기 애호가를 위해 Chrome, Firefox 및 Edge에서 작동하는 요리 된 다운로드 기능이 있습니다.

window.saveFile = function (bytesBase64, mimeType, fileName) {
var fileUrl = "data:" + mimeType + ";base64," + bytesBase64;
fetch(fileUrl)
    .then(response => response.blob())
    .then(blob => {
        var link = window.document.createElement("a");
        link.href = window.URL.createObjectURL(blob, { type: mimeType });
        link.download = fileName;
        document.body.appendChild(link);
        link.click();
        document.body.removeChild(link);
    });
}

createObjectURL제 2의 인수 ... 동의하지 않습니다
끝없는

5

프로젝트에 하나의 종속성을 추가 할 수 있다면 편리한 기능 을 제공하는 훌륭한 blob-utilnpm 패키지base64StringToBlob있습니다. package.json당신에 추가되면 다음과 같이 사용할 수 있습니다 :

import { base64StringToBlob } from 'blob-util';

const contentType = 'image/png';
const b64Data = 'iVBORw0KGgoAAAANSUhEUgAAAAUAAAAFCAYAAACNbyblAAAAHElEQVQI12P4//8/w38GIAXDIBKE0DHxgljNBAAO9TXL0Y4OHwAAAABJRU5ErkJggg==';

const blob = base64StringToBlob(b64Data, contentType);

// Do whatever you need with your blob...

3

Base64 변환을보다 선언적인 방식으로 게시하고 있습니다. 비동기가있는 동안 fetch().blob()많은이 솔루션과 같은 매우 깔끔한 내가, 그것은 인터넷 익스플로러 11에서 작업을하지 않는 (아마 에지 -이 하나를 테스트하지 않았습니다), 심지어 polyfill로 -에 내 의견을 살펴 가지고 끝없는을 ' 자세한 내용은 게시 하십시오.

const blobPdfFromBase64String = base64String => {
   const byteArray = Uint8Array.from(
     atob(base64String)
       .split('')
       .map(char => char.charCodeAt(0))
   );
  return new Blob([byteArray], { type: 'application/pdf' });
};

보너스

인쇄하려면 다음과 같은 작업을 수행하십시오.

const isIE11 = !!(window.navigator && window.navigator.msSaveOrOpenBlob); // Or however you want to check it
const printPDF = blob => {
   try {
     isIE11
       ? window.navigator.msSaveOrOpenBlob(blob, 'documents.pdf')
       : printJS(URL.createObjectURL(blob)); // http://printjs.crabbly.com/
   } catch (e) {
     throw PDFError;
   }
};

보너스 x 2-Internet Explorer 11의 새 탭에서 BLOB 파일 열기

서버에서 Base64 문자열의 일부 전처리를 수행 할 수있는 경우 URL 아래에 표시하고 다음 링크를 사용할 수 있습니다. printJS:)


2

다음은 JavaScript로 쉽게 변환하여 사용할 수있는 TypeScript 코드입니다.

/**
 * Convert BASE64 to BLOB
 * @param Base64Image Pass Base64 image data to convert into the BLOB
 */
private convertBase64ToBlob(Base64Image: any) {
    // Split into two parts
    const parts = Base64Image.split(';base64,');

    // Hold the content type
    const imageType = parts[0].split(':')[1];

    // Decode Base64 string
    const decodedData = window.atob(parts[1]);

    // Create UNIT8ARRAY of size same as row data length
    const uInt8Array = new Uint8Array(decodedData.length);

    // Insert all character code into uInt8Array
    for (let i = 0; i < decodedData.length; ++i) {
        uInt8Array[i] = decodedData.charCodeAt(i);
    }

    // Return BLOB image after conversion
    return new Blob([uInt8Array], { type: imageType });
}

4
이 코드 스 니펫이 해결책이 될 수 있지만 설명을 포함하면 게시물의 품질을 향상시키는 데 실제로 도움이됩니다. 앞으로 독자에게 질문에 대한 답변을 제공하고 있으며 해당 사람들이 귀하의 코드 제안 이유를 모를 수도 있습니다.
Johan

2
또한 왜 의견을 말하고 있습니까?
canbax

4
귀하의 Typescript code코드는 하나의 유형이 그 유형입니다 any. 왜 귀찮게?
zoran404

0

페치가있는 메소드가 가장 좋은 솔루션이지만, 누군가가 페치없이 메소드를 사용해야하는 경우 이전에 언급 한 메소드가 나를 위해 작동하지 않았으므로 여기에 있습니다.

function makeblob(dataURL) {
    const BASE64_MARKER = ';base64,';
    const parts = dataURL.split(BASE64_MARKER);
    const contentType = parts[0].split(':')[1];
    const raw = window.atob(parts[1]);
    const rawLength = raw.length;
    const uInt8Array = new Uint8Array(rawLength);

    for (let i = 0; i < rawLength; ++i) {
        uInt8Array[i] = raw.charCodeAt(i);
    }

    return new Blob([uInt8Array], { type: contentType });
}
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.