[Web] File, Blob에 대하여
최근에 electron으로 영상 input을 녹화하는 어플리케이션을 만들어야 하는 일이 있었다. node.js 환경에서 node API인 fs, Buffer 등을 이용한 파일관리는 익숙했었는데 web API를 이용한 파일관리는 익숙하지가 않았어서 web API를 알아보면서 공부했던 것들을 남겨보려 한다.
브라우저의 파일 관리 API
File
파일 업로드 기능이 있는 웹페이지를 만들 때 File API를 사용했던 적이 있다. input 엘리먼트의 type이 file인 경우 files 프로퍼티를 이용해 File 객체를 가져올 수 있다.
<input type="file"/>
<script>
document.querySelector('input').onchange = e => {
console.log(e.target.files[0])
}
</script>
이 File 객체의 name
, size
, type
프로퍼티를 사용해서 목적에 맞는 유용한 웹 어플리케이션을 만들 수 있다. File은 또다른 web API인 Blob의 확장이다. 여기서 좀 더 복잡한 기능을 하는 웹 어플리케이션을 만들고자 한다면 Blob에 대해서 자세히 알아야 한다.
Blob
Blob은 ArrayBuffer의 확장이라고 볼 수 있으며, blobParts
와 type
으로 이루어져있다. blobParts
는 또다른 Blob, BufferSource(ArrayBuffer, ArrayBufferView) 또는 string의 배열이고, type
은 MIME 타입 문자열이다. Blob은 다음처럼 생성할 수 있다.
new Blob(['Hello, ', 'World!'], { type: 'text/plain' })
Blob {size: 13, type: “text/plain”}
text()
라는 메소드를 이용해 블롭의 내용을 UTF-8 문자열로 변환한 값을 반환하는 Promise를 생성할 수 있다.
const blob = new Blob(['Hello, ', 'World!'], { type: 'text/plain' })
blob.text().then(console.log)
Hello World!
위에서 만든 Blob을 URL.createObjectURL()
메소드를 이용해서 마치 'Hello, World!'
라는 텍스트 파일을 생성한 것처럼 다운로드할 수 있게 만들 수도 있다.
<a download="test.txt" href=#>click</a> to download
<script>
const blob = new Blob(['Hello, ', 'World!'], {type: 'text/plain'});
const blobURL = URL.createObjectURL(blob); // blob:http://{host}/{DOMString}
document.querySelector('a').href = blobURL;
</script>
click
앵커 엘리먼트를 클릭하면 Hello, World!
라는 텍스트가 담긴 test.txt 파일이 다운로드 된다.
Blob은 buffer처럼 다룰 수 있기 때문에 slice()메소드를 이용해서 일부분을 추출해낼 수도 있다. 다음 코드는 Hello, World!
텍스트 블롭에서 Hello
만 잘라낸 블롭을 만든다.
const blob = new Blob(['Hello, ', 'world!'], {type: 'text/plain'});
const helloBlob = blob.slice(0, 5, { type: 'text/plain' })
이곳에서 Blob의 자세한 정의와 사용법, Chrome의 Blob 메모리 정책 등을 더 자세히 알아볼 수 있다. (Blob을 자세히 다룰거라면 꼭 한번 읽어보는 것을 추천한다)
MediaRecorder로 화면 녹화하기
Blob에 대한 이해가 필요했던 것이 Web API인 MediaRecorder를 이용해 영상을 녹화할 때, 영상의 chunk가 Blob 형태로 전달되기 때문에 이 chunk를 다루기 위해서는 Blob을 다룰줄 알아야 했기 때문이다. 간단한 영상 녹화 웹 어플리케이션을 만들어 보자.
<body>
<video id="video-input" autoplay></video>
<button id="btn-record">Record</button>
<script>
navigator.mediaDevices.getUserMedia({ video: true })
.then(record)
function record(stream) {
const video = document.getElementById("video-input")
video.srcObject = stream
}
</script>
</body>
위 html스크립트를 작성하면(영상 input 장치가 있고, 브라우저에서 권한을 설정했다면) 화면에 영상 input을 video태그를 통해 확인할 수 있다. record
함수에 MediaRecorder를 사용하는 녹화 로직을 추가하자.
function record(stream) {
const video = document.getElementById("video-input")
video.srcObject = stream
let chunks = [] // Blob들을 저장할 array
// recorder 이벤트 리스너 등록
const recorder = new MediaRecorder(stream)
recorder.ondataavailable = (e) => { // 1
chunks.push(e.data) // Blob 인스턴스
}
recorder.onstop = () => {
const blob = new Blob(chunks, {type: 'video/webm'}); // 2
const blobURL = URL.createObjectURL(blob);
chunks = []
const download = document.createElement("a") // 3
download.href = blobURL;
download.download = "my_video.webm"
download.click()
}
// 녹화 버튼 이벤트 리스너 등록
const btnRecord = document.getElementById("btn-record")
btnRecord.onclick = () => {
if (recorder.state === "recording") {
recorder.stop()
btnRecord.innerText = "Record"
return
}
recorder.start()
btnRecord.innerText = "Stop"
}
}
MediaRecorder에 대한 자세한 정의는 여기에서 확인할 수 있다. 현재 Chrome 브라우저에서는 .webm
비디오 형식만 사용할 수 있다. Record 버튼 클릭 시 MediaRecorder에서 input 영상을 녹화하기 시작하고, start() 메소드 호출 시 timeslice를 매개변수로 넘겨줬다면 매 timeslice마다, 아니라면 stop() 메소드가 호출됐을 때 ondataavailable
이벤트가 발생한다.
- 이때 이벤트의 data 프로퍼티가 영상의 blob인데, 이 blob을 배열에 담아둔다.
- stop 이벤트 호출 시 하나의 새로운 blob으로 생성한 뒤 objectURL로 만들어준다.
- 그 다음 사용자의 컴퓨터에 my_video.webm 파일을 다운로드 하게 된다.
생성된 .webm
영상 파일은 메타데이터가 불완전하기 때문에 탐색이 불가능하다. 따라서, ts-ebml
라이브러리나 ffmpeg
을 이용해서 탐색 가능한 영상 파일로 변환을 해주어야 한다.
References
https://developer.mozilla.org/ko/docs/Web/API/File https://developer.mozilla.org/ko/docs/Web/API/Blob https://javascript.info/blob