티스토리 뷰

javascript/HTTP 스크립팅

Blob

양은냄비 2016.12.14 06:54

Blob은 일련의 데이터를 처리하거나 간접 참조하는 객체다. Blob이란 이름은 SQL 데이터베이스에서 유래하였으며 '대형 이진 객체(Binary Large Object)'를 의미한다. 자바스크립트에서 Blob은 흔히 이진 데이터를 나타내며 해당 데이터의 크기가 매우 클 수 있지만, 두 가지 특징 모두 강제된 사항은 아니다. 즉, 작은 텍스트 파일의 내용도 Blob으로 나타낼 수 있다. Blob은 대개 바이트의 크기를 알아내거나, 해당 MIME 타입이 무엇인지 요청하며, 데이터를 작은 Blob으로 잘게 나누는 등의 작업에 사용된다. 즉, 데이터 자체라기보다는 데이터를 간접적으로 접근하기 위한 객체인 것이다.


//Blob을 획득하는 방법은 나중에 살펴볼 것이다.

var blob = ... 

//Blob의 바이트 단위 크기

blob.size

//Blob의 MIME 타입을 저장하거나, 알 수 없으면 ""를 저장한다.

blob.type

//Blob의 첫 1킬로바이트를 텍스트로 가져온다.

var subblob = blob.slice(0, 1024, "text/plain");

//Blob의 마지막 1킬로바이트를 타입 없이 가져온다.

var last = blob.slice(blob.size - 1024, 1024);


웹브라우저는 메모리 또는 디스크에 Blob을 저장할 수 있으며, Blob은 비디오 파일과 같이 매우 커서, 메모리에 적재하려면 slice()를 활용하여 작은 조각으로 먼저 분리해야 할 수도 있다. 데이터의 크기가 매우 크기 때문에 디스크를 사용해야 하므로, Blob API는 비동기 방식으로 동작한다. (워커 스레트에서는 동기 방식으로 동작 가능하다.)


Blob으로써의 파일

로컬 파일 접근을 허용하는 브라우저에서 <input type="file">요소의 files 프로퍼티는 FileList 객체일 것이다. 이 유사 배열 객체는 사용자가 선택한 0개 이상의 File 객체 목록이다. File 객체는 name과 lastModifiedDate 프로퍼티가 존재하는 Blob 객체다.

<script>

//선택된 파일의 목록에 관한 로그 정보

function fileinfo(files) {

//files는 유사 배열 객체다.

for(var i=0; i<files.length; i++) {

var f = files[i];

console.log(f.name,    //경로를 제외한 파일명

f.size, f.type,    //size와 type은 Blob 프로퍼티다.

f.lastModifiedDate);    //lastModifiedDate는 File 프로퍼티다.

}

}

</script>

<!-- 여러 개의 이미지 파일을 선택할 수 있고, 선택 목록을 fileinfo()로 전달한다. -->

<input type="file" accept="image/*" multiple onchange="fileinfo(this.files)" />


Blob 다운로드

XMLHttpRequest를 활용한 Blob 다운로드하기

//GET 방식으로 url의 내용을 Blob으로 가져온 다음 지정한 콜백으로 전달한다.

//이 코드는 현 시점에 해당 API를 지원하는 브라우저가 존재하지 않기 때문에 테스트하지는 못했다.

function getBlob(url, callback) {

//새 XHR 객체를 생성한다.

var xhr = new XMLHttpRequest();

//가져올 URL을 지정한다.

xhr.open("GET", url);

//blob 형식으로 부탁합니다.

xhr.responseType = "blob";

//onreadystatechange보다 onload가 더 다루기 쉽다.

xhr.onload = function() {

//blob을 콜백으로 전달한다.

callback(xhr.response);

//주의! responseText가 아니라 response이다.

}

//이제 요청을 전송한다.

xhr.send(null);

}


Blob 만들기

Blob은 흔히 로컬 파일이나 URL, 데이터베이스와 같이 외부 출처로부터 가져온 데이터들을 나타낸다. 그러나 웹에 업로드하거나 파일 혹은 데이터베이스에 저장하거나 다른 스레드로 전달하기 위해서 웹 애플리케이션 고유의 Blob을 생성해야 할 수도 있다. 데이터에서 Blob을 생성하려면 BlobBuilder를 사용하면 된다.

//새 BlobBuilder를 생성한다.

var bb = new BlobBuilder();


//blob에 문자열을 덧붙이고 NULL 문자를 활용하여 문자열의 끝을 표시한다.

bb.append("이 blob은 이 텍스트와 부호가 있는 32비트 빅 엔디언 정수 10개를 포함한다.");

//문자열의 끝부분을 표시하는 null 문자로 종료한다.

bb.append("\0");


//ArrayBuffer에 몇 가지 데이터를 저장한다.

var ab = new ArrayBuffer(4*10);

var dv = new DataView(ab);

for(var i=0; i<10; i++) dv.setInt32(i*4, i);


//Blob에 이 ArrayBuffer를 덧붙인다.

bb.append(ab);


//이제 빌더로부터 blob을 가져온 다음, MIME 타입을 지정한다.

var blob = bb.getBlob("x-optional/mime-type-here");


/*

Blob을 작은 조각으로 나누는 slice() 메서드를 살펴보았다. 이러한 Blob들을 하나로 결합하려면, 이들을 BlobBuilder의 append() 메서드로 전달하면 된다.

*/


Blob URL

가져오거나 생성한 Blob을 활용하여 실제로 무엇을 할 수 있는지 이야기해 보자. Blob로 할 수 있는 가장 간단한 형태 중 하나는 Blob을 가리키는 URL을 생성하는 것이다. 이러한 URL을 생성해 두면, DOM이나 스타일시트 또는 XMLHttpRequest의 목적 URL과 같이 일반 URL을 사용하는 곳 어디라도 Blob URL을 사용할 수 있다.

Blob URL은 createObjectURL()함수를 활용하여 생성한다. 


Blob URL은 영구적이지 않다. Blob URL은 해당 URL을 생성한 문서를 벗어나거나 종료하면 더 이상 유효하기 않는다. 예를 들어, Blob URL을 로컬 스토리지에 저장해두고 다음 사용자가 웹 애플리케이션의 새로운 세션을 시작하였을 때 재활용할 수 있게 하기란 불가능하다.


URL.revokeObjectURL() 또는 webkitURL.revokeObjectURL()을 호출함으로써 수동적으로 Blob URL의 효력을 '폐기'시킬 수도 있다. 이것은 메모리 관리에 관한 문제다. 썸네일 이미지가 한번 출력되면 더 이상 Blob이 필요하지 않기 때문에 가비지 컬렉션의 대상이 되어야 한다. 


드롭 & 드롭 예제

<!DOCTYPE html>

<html><head>

<script>

var getBlobURL = (window.URL && URL.createObjectURL.bind(URL)) ||

(window.webkitURL && webkitURL.createOjbectURL.bind(webkitURL)) ||

window.createObjectURL;

var revokeBlobURL = (window.URL && URL.revokeObjectURL.bind(URL)) ||

(window.webkitURL && webkitURL.revokeObjectURL.bind(webkitURL)) ||

window.revokeObjectURL;


//문서가 로드되면, droptarget 요소에 이벤트 핸들러를 추가하여,

//파일 드롭을 처리할 수 있게 한다.

window.onload = function() {

//핸들러를 추가할 요소를 찾는다.

var droptarget = document.getElementById("droptarget");

//사용자가 droptarget 위에서 파일을 드래그하기 시작하면, droptarget을 돋보이게 한다.

droptarget.ondragenter = function(e) {

//파일을 아닌 다른 것이 드래그 되면 무시한다.

//HTML5의 dropzone 속성이 구현된다면 이 작업이 더 수월해질 것이다.

var types = e.dataTransfer.types;

if(!types || 

(types.contains && types.contains("Files")) ||

(types.indexOf && types.indexOf("FIles") != -1)) {

//droptarget을 돋보이게 한다.

droptarget.classList.add("active");

return false;

}

};


//사용자가 드래그 객체를 드롭 영역 밖으로 이동시키면, 돋보이지 않게 한다.

droptarget.ondragleave = function() {

droptarget.classList.remove("active");

};


//이 핸들러는 브라우저에게 드래그 중임을 알리는 역할을 한다.

droptarget.ondragover = function(e) { return false; };


//사용자가 드롭 영역에 파일을 놓으면, 해당 파일의 URL을 얻어서 썸네일을 출력한다.

droptarget.ondrop = function(e) {

//드롭된 파일 목록

var files = e.dataTransfer.files;

//전체 목록을 순회한다.

for(var i=0; i<files.length; i++) {

var type = files[i].type;

//이미지가 아닌 것은 무시한다.

if(type.substring(0,6) !== "image/") continue;

//<img>요소를 생성한다.

var img = document.createElement("img");

//Blob URL을 <img>에 사용한다.

img.src = getBlobURL(files[i]);

//이미지가 로드되면

img.onload = function() {

//이미지의 크기를 조정하고

this.width = 100;

//문서에 삽입한다.

document.body.appendChild(this);

//메모리 누수를 방지한다.

revokeBlobURL(this.src);

}

}


//droptarget을 돋보이지 않게 한다.

droptarget.classList.remove("active");

//드롭이 처리되었다.

return false;

};

};

</script>

<style>

/* 파일 드롭 타깃에 대한 간단한 스타일 */

#droptarget { border : solid black 2px; width : 200px; height : 200px; }

#droptarget.active { border : solid red 4px; }

</style>

</head>

<body>

<!-- 문서는 파일 드롭 타깃으로만 시작한다. -->

<div id="droptarget">여기에 이미지 파일을 드롭하십시오.</div>

</body>

</html>


Blob 읽어 들이기

FileReader 객체는 Blob에 포함된 문자나 바이트들을 읽을 수 있게 하며 BlobBuilder와는 반대의 성격을 가진 것이라고 생각할 수 있다(단지 파일만이 아니라 어떠한 Blob을 사용해도 작업이 가능하기 때문에 BlobReader라고 부르는 편이 더 나았겠다). Blob은 매우 큰 크기의 객체를 파일 시스템에 저장할 수 있으므로, Blob의 내용을 읽기 위한 FileReader 객체 API는 XMLHttpRequest API와 매우 비슷하게도 비동기적이다. 


FileReader를 활용하여 텍스트 파일 읽기

<script>

//지정한 텍스트 파일을 읽은 다음, 아래의 <pre> 요소에 출력한다.

function readfile(f) {

//FileReader 객체를 생성한다.

var reader = new FileReader();

//파일을 읽어 들인다.

reader.readAsText(f);

//이벤트 핸들러를 정의한다.

reader.onload = function() {

//파일의 내용

var text = reader.result;

//output 요소를 가져온다.

var out = document.getElementById("output");

//요소의 내용을 비운다.

out.innerHTML = "";

//파일 내용을 출력한다.

out.appendChild(document.createTextNode(text));

}


//뭔가 잘못되었다면

reader.onerror = function(e) {

//로그로 기록한다.

console.log("Error", e);

}

}

</script>


출력할 파일을 선택하십시오 :

<input type="file" onchange="readfile(this.files[0])"></input>

<pre id="output"></pre>


readAsArrayBuffer() 메서드는 readAsText()와 비슷하지만, 파일 내용을 문자열 형태보다는 ArrayBuffer 형태로 다룰 때 좀더 적합하다. 아래의 예제는 readAsArrayBuffer()를 사용하여 파일의 첫 4바이트를 빅 엔디언 정수로 읽어 들이는 코드다.

<script>

//지정한 blob의 첫 4바이트를 가져온다.

//가져온 이 '매직 넘버'로 파일의 타입을 식별할 수 있으면,

//Blob의 verified_type 프로퍼티를 비동기적으로 설정한다.

function typefile(file) {

//파일의 시작 부분만을 잘라낸다.

var slice = file.slice(0,4);

//비동기 FileReader를 생성한다.

var reader = new FileReader();

//파일의 조각을 읽어 들인다.

reader.readAsArrayBuffer(slice);

reader.onload = function(e) {

//결과 ArrayBuffer

var buffer = reader.result;

//결과 바이트의 접근 권한을 획득한다.

var view = new DataView(buffer);

//빅 엔디언으로 4바이트를 읽어 들인다.

var magic = view.getUint32(0, false);

//이 내용으로 파일의 타입을 결정한다.

switch(magic) {

case 0x89504E47: file.verified_type = "image/png"; break;

case 0x47474638: file.verified_type = "image/gif"; break;

case 0x25504446: file.verified_type = "application/pdf"; break;

case 0x504b0304: file.verified_type = "application/zip"; break;

}


console.log(file.name, file.verified_type);

};

};

</script>

<input type="file" onchange="typefile(this.files[0])"></input>





'javascript > HTTP 스크립팅' 카테고리의 다른 글

Blob  (0) 2016.12.14
web worker  (0) 2016.12.13
XMLHttpRequset  (0) 2016.11.19
Ajax  (0) 2016.11.17
WebSocket  (0) 2016.11.15
JSONP  (0) 2016.11.15
TAG
댓글
댓글쓰기 폼