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.