안녕하세요. 이번 시간에는 HTML5와 자바스크립트로 파일을 읽는 File API에 대해 알아보겠습니다!
파일을 읽는 김에 쓰기도 하지, 왜 읽기만 하냐고요? 브라우저가 사용자의 컴퓨터에 파일을 쓰게 되면 매우 위험할 수 있기 때문입니다. 보통 하루에도 수 십, 많으면 수 백 페이지까지 웹을 돌아다니시죠? 수 백 개의 페이지 중에는 위험한 사이트도 있을 수 있습니다. 만약 파일 쓰기 기능이 있다면 사이트에 접속했을 때, 바로 그 사이트가 바이러스 파일을 여러분의 컴퓨터에 심어버릴 수도 있겠죠?
그런데 파일 읽기가 되면, 여러분의 컴퓨터에 있는 소중한 자료들을 훔쳐보는 거 아닌가 걱정되실 수도 있겠네요. 걱정하시 마세요! 자신이 허용한 파일만 읽을 수 있습니다. 가장 쉬운 예로 파일 업로드 인풋 박스가 있죠. 인풋 박스를 누르면 파일 선택 창이 뜨는데요. 이렇게 여러분이 선택한 파일만 브라우저가 읽을 수 있습니다.
가장 흔히 하는 파일 인풋박스로 알아보죠. 자바스크립트로 선택한 후, onchange 이벤트 핸들러를 붙여, 파일을 선택할 때마다 콜백 함수가 호출되게 해줍니다. 인풋박스에 multiple을 붙이면 여러 파일을 업로드할 수 있습니다.
<input type="file" id="upload" multiple />
var file = document.getElementById('upload');
file.onchange = function(e) {
var files = e.target.files; // FileList 객체
console.log(files); // { 0: File, 1: File, length: 2 }
console.log(files[0]); // 아래에 적어놨습니다.
}
위의 예시는 두 개의 사진을 올렸을 때 발생하는 이벤트입니다. e.target
은 이벤트가 일어난 대상, 즉 input 자신을 가리키고요. 그 아래 files에 어떤 파일들을 올렸는지 나옵니다. 콘솔로 찍어보니 유사배열이네요. (배열처럼 생겼지만 배열은 아닌 객체입니다)
0번, 1번 키에 File 객체가 들어있습니다. File 객체는 다음과 같이 생겼습니다.
{
name: 'zerocho.png', // 파일 이름
size: 74120, // byte 단위 파일 크기
lastModified: 1495791249810, // 올린 시간 timestamp
type: 'image/png'
}
오~ 파일에 대한 정보가 들어있죠? 그런데 정작 중요한 파일 데이터 자체가 없습니다. 사실 없는 건 아니고 숨어있습니다. 이 숨어있는 데이터를 읽으려면 FileReader API를 사용해야 합니다. window.FileReader
에 위치합니다. new FileReader()
로 파일리더를 만들어준 후 사용하면 됩니다.
파일을 읽는 방법은 네 가지가 있습니다. readAsText, readAsDataURL, readAsArrayBuffer, readAsBinaryString이죠. 하나씩 알아봅시다. 참고로 FileReader가 즉시 파일을 읽는 게 아니기 때문에 onload 이벤트 핸들러를 붙여서 콜백으로 파일을 다 읽었다는 것을 알려주도록 해야 합니다.
readAsText
file.onchange = function(e) {
var fileReader = new FileReader();
fileReader.readAsText(e.target.files[0]);
fileReader.onload = function(e) {
console.log(e.target.result);
}
}
네 외계어가 나옵니다. 유일하게 알아들을 수 있는 말은 처음에 나오는 png뿐이네요. 당연한 게 이미지 파일을 텍스트로 읽으라고 했으니 외계어가 나올 수밖에 없습니다. 텍스트 파일을 읽을 때 사용합시다.
readAsDataURL
file.onchange = function(e) {
var fileReader = new FileReader();
fileReader.readAsDataURL(e.target.files[0]);
fileReader.onload = function(e) {
console.log(e.target.result);
}
}
데이터 URL로 만드는 방법입니다. 처음에 base64라는 말이 보이시나요? base64로 인코딩했다는 뜻인데, base64로 인코딩한 경우 브라우저가 이 문자열을 인식해서 원래 데이터로 만들어줍니다. 길긴 해도 이 문자열을 주소창에 치면, 브라우저가 이 파일을 표시합니다. 즉, 파일 정보를 주소처럼 활용할 수 있다는 것이죠. img 태그의 src로도 사용할 수 있습니다.
따라서 이미지를 올리고 바로 미리보기를 표시하고 싶을 때, document.getElementById('이미지').src = e.target.result;
해주면 됩니다.
readAsArrayBuffer
file.onchange = function(e) {
var fileReader = new FileReader();
fileReader.readAsArrayBuffer(e.target.files[0]);
fileReader.onload = function(e) {
console.log(e.target.result); // ArrayBuffer 객체
}
}
얘는 조용하게 객체 하나를 반환합니다. ArrayBuffer라는 객체인데요. 버퍼링이라고 들어보셨나요? 그것처럼 데이터를 일정한 크기로 조금 조금씩 서버로 보낼 수 있습니다. 서버로 보낼 때 사용하면 됩니다.
readAsBinaryString
file.onchange = function(e) {
var fileReader = new FileReader();
fileReader.readAsBinaryString(e.target.files[0]);
fileReader.onload = function(e) {
console.log(e.target.result);
}
}
슬프게도 얘는 다시 외계어를 반환합니다. 이름처럼 이진 데이터를 반환하기 때문에 서버같은 곳에서 읽을 수 있습니다.
그럼 이미지 업로드를 할 때 항상 위와 같이 FileReader를 사용해서 ArrayBuffer나 BinaryString으로 해야 할까요? 아닙니다. 대부분의 경우 form의 enctype 속성을 multipart/form-data로 지정해주면 알아서 서버에 formData로 인코딩되어 넘어갑니다. 넘어간 데이터의 처리는 서버에 맡기면 됩니다!
참고로 FileReader는 File 객체 외에도 Blob이라는 Binary large object 형식의 데이터도 읽을 수 있습니다.
다음 시간에는 편하게 태그를 이동시키거나 파일을 업로드하는 Drag & Drop에 대해 알아보겠습니다!