이 게시글에서는 Javascript를 이용해 파일을 바이너리 데이터로 변환하거나 그 반대의 경우에 어떤 개념들이 이용되는지와 어떻게 해야하는지를 설명하고 있습니다.
File
Javascript에서 파일 데이터를 처리하기 위해 지원하는 인터페이스입니다. Blob의 한 종류로 Blob의 여러 기능을 사용할 수 있습니다. 아래와 같이 HTML의 File의 값을 받아올 때에도 해당 유형의 데이터로 받을 수 있습니다.
개발자 도구로 확인해보게 되면 name, type 등을 속성으로 가지나 데이터의 내용을 보여주지는 않습니다.
const file = document.querySelector('#file').files[0];
File(또는 Blob)의 내용을 읽어오기 위해서는 FileReader라는 인터페이스를 이용해야 합니다.
FileReader
FileReader는 비동기적으로 데이터를 읽어오기 위해 읽을 파일을 선택하고 이 내용을 버퍼로 읽어 컴퓨터에 저장할 수 있게 해줍니다. 텍스트, 이미지, 오디오 등의 멀티미디어 데이터를 바이너리 데이터로 변환할 때 사용합니다.
파일을 읽는 중에 에러가 발생하는 경우 error 속성을 참고할 수 있습니다.
이벤트 핸들러
- onabort: 읽기 동작이 중단될 때마다 발생
- onerror: 읽는 도중 에러가 생길 때마다 발생
- onload: 읽기 동작이 성공적으로 완료되었을 때마다 발생
- onloadstart: blob을 읽는 동작이 실행될 때마다 발생
- onloadend: blob을 읽는 동작이 끝날 때마다 발생(성공 여부는 상관없음)
- onprogress: blob을 읽는 중에 발생
주요 메서드
- readAsArrayBuffer(): blob을 읽기 시작합니다. 동작이 완료되면 결과에는 파일 데이터가 포함된 ArrayBuffer를 반환됩니다.
- readAsBinaryString(): blob을 읽기 시작합니다. 동작이 완료되면 결과에는 raw binary data가 문자열 형태로 반환됩니다.
- readAsDataURL(): blob을 읽기 시작합니다. 동작이 완료되면 파일 데이터를 나타내는 data:URL이 포함됩니다.
- readAsText(): blob을 읽기 시작합니다. 동작이 완료되면 파일 내용이 텍스트 문자열로 반환됩니다.
여기까지의 내용으로 파일 데이터를 받아온다고 했을 때 아래와 같이 작성할 수 있을 것입니다.
<input type="file" id="file">
<script>
const file = document.querySelector('#file').files[0];
const fileReader = new FileReader();
fileReader.readAsArrayBuffer(file);
fileReader.onload = function(evt) {
let viewArray = evt.target.result;
}
</script>
그러나, 여기서 받아온 데이터는 완전한 바이너리 데이터가 아닙니다. 제가 만약 엑셀 파일을 업로드 해서 이를 서버에 저장하는 기능을 개발해야한다면, 파일 리더의 메서드 중에서도 readAsArrayBuffer()를 사용해야 할 것입니다.
ArrayBuffer
readAsArrayBuffer는 읽기가 완료될 때 ArrayBuffer 형태의 데이터를 반환한다고 했습니다.
ArrayBuffer는 고정된 크기의 메모리에 바이너리 데이터를 저장하는 객체를 일종의 버퍼입니다. 일반적으로 버퍼 내부의 데이터를 쓰고 읽기 위해서는 ArrayBufferView라고 불리는 객체를 따로 생성해서 ArrayBuffer의 데이터에 접근 및 조작해야 합니다.
ArrayBufferView
이 때, 이 ArrayBufferView는 또다시 TypedArray와 DataView로 나뉘어집니다. TypedArray는 ArrayBuffer 내부의 바이너리 데이터를 이용해 만든 배열로, 타입이 정해져 있는 배열입니다.
- Uint8Array, Uint16Array, Float32Array 등 타입(Int, UnsingedInt, BigInt, Float)과 크기(8, 16, 32, 64 bit)를 지정할 수 있습니다.
- 요소의 갯수가 고정되어 있으므로 push(), pop() 등의 요소 추가/제거 메서드는 사용할 수 없으며, 기존에 저장된 데이터의 값만 변경할 수 있습니다.
흔히 사용되는 타입 중 하나가 uint8Array(8비트 부호 없는 정수)입니다. 이렇게 ArrayBufferView로 바이너리 데이터에 접근해 이 데이터를 데이터를 버퍼에 담아 이 버퍼를 다시 blob으로 변환하는 작업이 필요합니다.
Blob(Binary Large Object)
생성자를 통해 객체를 생성할 수 있습니다. 생성자의 첫 인자로는 ArrayBuffer가 들어가고, 두번째로는 관련 프로퍼티가 들어가게 되는데 필수적으로 type을 넣어주어야 합니다.
let blob = new Blob(arrayBuffer, {type: 'image/png'});
이 때 위에서 만든 ArrayBuffer를 넣어봅시다.
// ...
fileReader.onload = function(evt) {
let viewArray = evt.target.result;
let blob = new Blob(viewArray, {type: 'image/png'});
}
위와 같이 읽어온 File 내용을 blob으로 변경할 수 있습니다. 그런데 보통 서버로 멀티미디어 데이터를 전송할 때는 이 ArrayBuffer를 그대로 사용하지 않고, 이를 base64로 인코딩하여 사용합니다.
Base64
Base64란, 바이너리 데이터를 문자 코드에 영향을 받지 않는 공통 ASCII 문자로 표현하는 바이너리-텍스트 인코딩 체계입니다. 일반적으로 바이너리 데이터 전송이 불가능한 멀티미디어 파일에 대해 전송 작업이 가능하도록 바이너리 데이터를 문자열로 인코딩할 때 사용됩니다. 이 작업을 통해 전송 중에 바이너리 데이터의 손실이 일어나지 않습니다.
Cf. String.fromCharCod
아스키코드 배열을 받아 이를 문자열로 변환해주는 역할을 수행합니다.
1. 다수의 아스키 코드를 인자로 전달하는 경우
String.fromCharCode(97, 98); // 'ab'
2. 아스키 코드 배열을 인자로 전달하는 경우
String.fromCharCode.apply(null, [97, 98]); // 'ab'
btoa()
바이너리 데이터 → ASCII 문자열
let array = new Uint8Array(biteArray); // ArrayBufferView로 변환
const encodeStr = String.fromCharCode.apply(null, array); // 유니코드 값 -> 문자열
let biteArray = btoa(encodeStr); // 문자열 -> ASCII 코드 배열
atob()
ASCII 문자열 → 바이너리 데이터
let charArray = atob(encodeStr);
그러면 앞서 설명한 내용들의 순서대로 File 내용을 FileReader.readAsArrayBuffer()를 통해 읽어와 반환되는 유니코드 집합을 다시 base64로 인코딩한 후 마지막으로 blob으로 변환하는 코드는 아래와 같습니다.
const file = documnet.querySelector('#file').files[0];
const fileReader = new FileReader();
fileReader.readAsArrayBuffer(file);
fileReader.onload(function(evt) {
const uniArray = evt.target.result;
const viewArray = new Uint8Array(uniArray);
const blob = new Blob(btoa(String.fromCharCode(null, viewArray)));
});
참고
'Frontend > Javascript' 카테고리의 다른 글
JavaScript) async 함수가 비동기적으로 값을 반환하도록 하기 (0) | 2023.05.11 |
---|---|
Javascript) File 변환 - 2. 바이너리 데이터를 File로 변환 (0) | 2023.03.19 |
Javascript) 배열(Array)의 중복 제거 (0) | 2022.11.18 |
Javascript) JqGrid custom edittype 사용하기 (0) | 2022.10.20 |
Javascript) draggable한 element를 드래그 시 화면 일부가 하얗게 변하는 현상 해결방법(Chrome 버전 106) (0) | 2022.10.13 |