File API 정리하기
최근 FileReader 를 사용할 일이 생겨 File API 를 정리 해보고자 한다.
HTML5 부터 브라우저는 File API 지원하기 시작한다.
File API 는 아래와 같이 정의되어 있다.
- FileList : 파일 리스트
- File : 파일 데이터
- FileReader : 파일 읽기
- Blob : 바이트 데이터
https://w3c.github.io/FileAPI/#FileReader-interface
간단예제
<!--html-->
<input id="input" type="file" accept="text/*">
<script>
var inputEl = document.querySelector('#input');inputEl.addEventListener('change', function (event) {
// FileList
var fileList = inputEl.files || event.target.files; // File
var file = fileList[0]; var reader = new FileReader(); reader.onload = function(progressEvent) {
console.log(progressEvent.target.result);
}; reader.readAsText(file);
});
</script>
```
해당 예제는 input[type=file]
로 입력받은 파일을 FileReader 로 내용을 Text 로 읽는 예제다.
input file type
기본적으로 브라우저에서는 보안상 로컬 파일에 직접 접근 할 수 없다.
input[type=file]
는 브라우저에서 유저가 직접 로컬의 파일을 선택할 수 있게 도와준다.
이렇게 선택한 파일은 File 로 정의되고 FileList 에 담기게 된다.
이때 multiple 설정 여부와 관계없이 ArrayLike 형태인 FileList 로 담긴다.
파일을 선택하면 input, change EventHandler 가 발생되며, 선택된 파일은 HTMLInputElement.files 에 저장된다.
oninput 와 onchange 는 input.files 이 변경될때, 발생하는 점은 유사 하다.
유일한 차이점은 onchange 는 포커스를 잃을 때 발생하고, oninput 은 요소 값이 변경된 직후에 발생한다.
때문에 순서상으로 oninput 이 먼저 발생한다.
input 의 속성
input[type=file]
은 value, accept, capture, files, multiple 속성을 갖을 수 있다.
- value [DOMString] : 파일 경로
- accept [MIME] : 사용 가능한 파일 종류
- capture [string] : 파일 캡처 방법
- multiple [boolean] : 여러 파일 선택 여부
- files [FileList] : 선택된 파일들
input.value
input[type=file]
value 에는 파일의 경로를 가진다.
브라우저에서는 보안상 로컬 파일에 직접 접근 할 수 없으며, 로컬 파일 구조의 노출을 막고자 C:\fakepath\
를 포함하여, 숨긴다.
이 경로의 브라우저마다 구현된 형태가 다를 수 있다.
https://html.spec.whatwg.org/multipage/input.html#fakepath-srsly
input.accept
input[type=file]
accept 는 선택 가능한 파일 종류를 설정할 수 있다.
파일은 ,
로 구분하며, 아래와 같은 형태로 작성할 수 있다.
accept="image/*"
: png 같은 이미지파일accept="video/*"
: mp4 같은 동영상파일accept="audio/*"
: wav 같은 오디오파일accept=".pdf, .doc, .csv"
: pdf, doc, css 확장자 파일
input.capture
input[type=file]
capture 는 모바일 같은 특정 기기에서 capture 방법을 설정할 수 있다.
capture="camera"
: 카메라capture="camcorder"
: 동영상capture="microphone"
: 마이크
capture 속성을 지원하지 않는 브라우저의 경우, accept="image/*;capture=camera"
으로 사용할 수도 있다.
<!--html-->
<input id="camera"
type="file"
capture="camera"
accept="image/*;capture=camera"
/>
FileList
input.files 에는 선택한 파일들을 FileList 로 가진다.
FileList 는 File 들을 가지는 객체이며, { [index]: File, length: number }
형태를 가진 array-like object 이다.
FileList[index]
혹은 FileList.item(index)
형태로 File 에 접근할 수 있다.
이 FileList 는 Symbol(Symbol.iterator)
가 정의되어 있어, 순차적으로 참조하기 위해서, for of
를 사용할 수 있다.
혹은 Array.from()
로 변환하여 참조 할 수 있다.
// javascript
function (event) {
var fileList = event.target.files; for(const file of fileList) {
// ...
} Array.from(fileList).forEach((file) => {
// ...
})
};
File
브라우저는 보안상 파일을 조작할 수 없다. 때문에 모든 값은 읽기 전용 이다.
File 은 아래 속성을 가질 수 있다.
- name : 파일 이름
- lastModified : 파일을 마지막으로 수정한 Unix Time
- lastModifiedDate : 파일을 마지막으로 수정한 Date 객체
- size : 파일의 크기 (Byte 값)
- type : MIME 유형
File
name: "htm_20190729104652375824.jpg"
lastModified: 1586075186723
lastModifiedDate: Sun Apr 05 2020 17:26:26 GMT+0900 (대한민국 표준시) {}
size: 54973
type: "image/jpeg"
File 은 Blob 을 확장하여 구현되었다.
Blob (Binary Large Object)
Blob 객체는 파일를 text 나 2진 데이터 형태로 읽을 수 있다.
Blob 은 아래 속성을 가질 수 있다.
- size : 파일의 크기 (Byte 값)
- type : MIME 유형
https://www.w3.org/TR/FileAPI/#blob-section
Blob 은 2가지 방법으로 생성할 수 있다.
- new Blob(
ArrayBuffer | Blob | DOMString
,{type?: MIME}
): Blob - Blob.slice(
start?
,end?
,contentType?
): Blob
Blob 의 바이트를 시작 및 끝 바이트 범위에서 복제해 새로운 Blob 객체를 생성하고 반환한다.
async function () {
const textBlob = new Blob(
['😀test txt'],
);
console.log( await textBlob.text() );
// 😀test txt const uintBlob = new Blob(
[new Uint8Array([240, 159, 152, 128, 116, 101, 115, 116, 32, 116, 120, 116])],
{type: 'text/plain'}
);
console.log( await uintBlob.text() );
// 😀test txt const obj = { test: '😀test txt'};
const jsonBlob = new Blob(
[JSON.stringify(obj, null, 2)],
{type : 'application/json'}
);
console.log( await jsonBlob.text() );
// {
// test: 😀test txt
// } const copyBlob1 = textBlob.slice();
const copyBlob2 = new Blob([copyBlob1]);
console.log( await copyBlob1.text() );
// 😀test txt
console.log( await copyBlob2.text() );
// 😀test txt
}
Blob 의 내용은 3가지 방법으로 읽을 수 있다.
- Blob.stream(): ReadableStream
- Blob.text(): Promise<UTF-8>
- Blob.arrayBuffer(): Promise<ArrayBuffer>
function (event) {
var file = event.target.files[0];
(async function (blob) {
const slice = blob.slice();
console.log(slice);
// Blob {size: 12, type: ""} const stream = blob.stream();
console.log(stream);
// ReadableStream {locked: false} const text = await blob.text();
console.log(text);
// 😀test txt const arrayBuffer = await blob.arrayBuffer();
console.log(arrayBuffer);
// ArrayBuffer(12) {}
// Int8Array(7498)
// Int16Array(3749)
// Uint8Array(7498)
// byteLength: 7498
})(file);
};
Blob ReadableStream 와 ArrayBuffer
Blob.stream()
은 ReadableStream 을 반환한다.
ReadableStream
은 Streams API
로 바이트 데이터 를 chunk 단위로 처리할 수 있게 도와준다.
ReadableStream.getReader()
는 read 를 요청하고 다른 stream 이 발생되지 않도록 lock 을 건다
read() 으로 반환된 Promise 에서 값을 조회할 수 있다.
new Blob(['😀test txt'])
.stream()
.getReader()
.read()
.then(({value, done}) => {
const uint8array = value;
return new TextDecoder('utf-8')
.decode(uint8array);
})
.then(console.log)
// 😀test txt
Blob.arrayBuffer()
는 ArrayBuffer 는 반환한다.
ArrayBuffer 는 수정할 수 없는 바이트로 구성된 배열 이다.
ArrayBuffer 는 TypedArray (Int8Array | Int16Array | Int32Array | Uint8Array 등
) 로 변환할 수 있다.
new Blob(['😀test txt'])
.arrayBuffer()
.then(arrayBuffer => {
return new TextDecoder('utf-8')
.decode(uint8array)
})
.then(console.log)
// 😀test txt
```
여기서 사용된 TextDecoder 는 텍스트를 UTF-8 | ISO-8859–2
같은 형태로 인코딩 혹은 디코딩 할 수 있게 도와주는 객체다.
TextDecoder.decode()
는 ArrayBuffer 를 설정한 형태의 Text 로 decode 할 수 있다.
Blob URL
URL.createObjectURL 를 이용하면, Blob 객체를 가리키는 URL 을 생성할 수 있다.
https://www.w3.org/TR/FileAPI/#blob-url
URL 은 blob:${url origin}/${UUID}
로 생성된다.ex) blob:https://example.org/9115d58c-bcda-ff47-86e5-083e9a2153041
해당 URL 은 생성된 탭에서만 쓸 수 있다.
Blob URL 은 페이지를 떠나기 전까지 유지되기 때문에, 필요하다면 revokeObjectURL
로 해지 해줄 수 있다.
var blob = new Blob([‘😀test txt’]);
var url = URL.createObjectURL(blob);
console.log(url);
// blob:http://localhost:8000/a1fa84dc-fd52-4435-bc22-b1cb2a7c5d84fetch(url)
.then(res => res.text())
.then((text) => {
URL.revokeObjectURL(url);
console.log(text);
// 😀test txt
});
해당 URL 은 a href 에 설정해서 다운로드 받을 수도록 설정할 수 있다.
이때 만약 revokeObjectURL
로 해지했다면 다운로드에 실패하게 된다.
<!--html-->
<a href="http://localhost:8000/a1fa84dc-fd52-4435-bc22-b1cb2a7c5d84"
download="file_name.txt"
>
download_button
</a>
Blob Canvas 와 createImageBitmap
Canvas 로 만든 Image 는 toBlob
으로 Blob 으로 만들 수 있다.
이때 기본 type 은 image/png
이다.
CanvasRenderingContext2D.drawImage()
를 이용하면, Blob 을 Canvas 에 그릴 수 도 있다.
이때 직접적으로 Blob 을 전달할 수 없고, Image 로 변환하거나, createImageBitmap 로 ImageBitmap
를 생성해서 전달해야 한다.
createImageBitmap 는 ImageData | Blob
을 전달 받아 ImageBitmap
를 생성할 수 있다.
<!--html-->
<canvas id="canvas" width="5" height="5"></canvas>
<script>
var canvas = document.getElementById('canvas');
var ctx = canvas.getContext('2d');var dataURL = canvas.toDataURL();
console.log(dataURL);
// data:image/png;base64,iVBORw0…canvas.toBlob(blob => {
console.log(blob)
// Blob {size: 72, type: "image/png"}
createImageBitmap(blob)
.then(imageBitmap => {
console.log(imageBitmap);
// ImageBitmap {width: 5, height: 5}
ctx.drawImage(img, 0, 0);
});
});
</script>
FileReader
FileReader 은 File 이나 Blob 의 내용을 읽을 수 있게 도와준다.
보안상 직접적인 Local Storage 에는 접근할 수 없다.
FileReader 에는 4가지 방법으로 파일을 전달 할 수 있다.
- readAsArrayBuffer(
file|blob
) [ArrayBuffer] - readAsBinaryString(
file|blob
) [0..255 범위의 문자열] - readAsDataURL(
file|blob
) [Base64] - readAsText(
file|blob
) [UTF-16|UTF-8 문자열]
FileReader 에서 전달 받은 파일을 읽기 성공하면 load EventLinser 에 등록한 function 이 호출된다.
이외에 loadstart | progress | loadend | error
로 읽는 상태에 따라 function 이 호출 된다.
function (event) {
const file = event.target.files[0];
const reader = new FileReader(); reader.onload = (progressEvent) => {
console.log(progressEvent.target.result);
}; reader.readAsArrayBuffer(file);
// readAsArrayBuffer ArrayBuffer(12) {} reader.readAsDataURL(file);
// readAsDataURL data:text/plain;base64,8J+YgHRlc3QgdHh0 reader.readAsBinaryString(file);
// readAsBinaryString ðtest txt reader.readAsText(file);
// readAsText 😀test txt
};
```
Network 와 File
XMLHttpRequest 와 Fetch 를 이용해 Network 상의 파일을 가져올 수 있다.
이때 XMLHttpRequest.responseType
의 설정 값에 따라 응답 데이터 유형 을 설정할 수 있다.
XMLHttpRequest.responseType
은 arraybuffer | blob | document | json | text
로 설정할 수 있으며 기본값을 text 이다.
var imageUrl = 'http://localhost:8000/image.jpg';var xhr = new XMLHttpRequest();
xhr.responseType = 'arraybuffer' || 'blob';xhr.onload = function(e) {
var arraybuffer = xhr.response || e.target.response;
}xhr.open('GET', imageUrl);
xhr.send();
fetch 는 응답 데이터 가 Response 객체 로 생성되어 전달된다.
Response 는 4 가지 변환 할 수 있다.
- Response.arrayBuffer()
- Response.blob()
- Response.json()
- Response.text()
만약 해당 형태로 변환하지 못한다면 SyntaxError
가 발생된다.
var imageUrl = ‘http://localhost:8000/image.jpg';fetch(imageUrl)
.then(res => res.blob())
.then(console.log);
// Blob {size: 75677, type: "image/jpg"}fetch(imageUrl)
.then(res => res.arrayBuffer())
.then(console.log);
// ArrayBuffer(75677) {}