April 30, 2020
์๋ฐ์คํฌ๋ฆฝํธ์์ ์ด๋ฏธ์ง๋ ๋์์, ์ค๋์ค ๋ฑ ๋์ฉ๋ ํ์ผ์ ์ ๋ก๋ํ ๋, file ๊ฐ์ฒด๋ฅผ ์ด์ฉํ๋ค. ์ด๋ฒ ํฌ์คํ ์์๋ file ๊ฐ์ฒด์ ๋ํ ๋ด์ฉ์ ์ ๋ฆฌํด๋ณด์๋ค.
DOM input ์๋ฆฌ๋จผํธ์ type ํ๋กํผํฐ์ ๊ธฐ๋ณธ๊ฐ์ type=text ์ด๋ค. ์ด ๋, ์ฌ์ฉ์๋ก๋ถํฐ ์
๋ ฅ๊ฐ์ string ํํ๋ก ๋ฐ๋๋ค.๊ทธ๋ผ ์ฌ์ฉ์๋ก๋ถํฐ file์ ์
๋ ฅ๋ฐ์ผ๋ ค๋ฉด ์ด๋ป๊ฒ ํ ๊น? file์ ์
๋ ฅ๋ฐ์ผ๋ ค๋ฉด input ์๋ฆฌ๋จผํธ์ type=file
๋ก ๋ช
์ํด์ฃผ๋ฉด ๋๋ค.
์ฌ์ฉ์๊ฐ ์ ๋ ฅํ ํ์ผ์ ์ ๊ทผํ๋ ค๋ฉด input ์๋ฆฌ๋จผํธ์ ์ ๊ทผํด์ files ํ๋กํผํฐ๋ฅผ ์ฐธ์กฐํ๋ฉด ๋๋๋ฐ, ์ง์ DOM์ ์ ๊ทผํด์ ์ ๊ทผํ ์๋ ์๊ณ , e.target ์ ํตํ์ฌ onChange ์ด๋ฒคํธํธ๋ค๋ฌ๋ฅผ ๋ฑ๋กํ์ฌ ์ ๋ ฅ๋ฐ์ ๊ฐ์ ๊ฐ์ ธ์ฌ ์๋ ์๋ค.
// ๋ฆฌ์กํธ hooks ์ฌ์ฉ
useEffect(() => {
const fileElem = document.getElementById('fileInput')
console.log(fileElem.files)
})
return <input type="file" id="fileInput" />
๐ฝ ์ฝ์ ์ถ๋ ฅํ๋ฉด
const handleImage = e => {
console.log(e.target.files)
}
return <input type="file" id="fileInput" onChange={handleFiles} />
๐ฝ ์ฝ์ ์ถ๋ ฅํ๋ฉด
ํ์ผ์ ์
๋ก๋ ํ ํ, file input ์๋ฆฌ๋จผํธ์ ์ ๊ทผํด์ files ํ๋กํผํฐ๋ฅผ ์ถ๋ ฅํด๋ณด๋ฉด fileList
๋ผ๋ ๊ฐ์ฒด๊ฐ ์ถ๋ ฅ๋๋ ๊ฒ์ ๋ณผ ์ ์๋ค. fileList ๊ฐ์ฒด ํ๋กํผํฐ๋ 0,1 โฆ ํํ์ ์ซ์๋ก, ๊ทธ๋ฆฌ๊ณ ๊ฐ์๋ File๊ฐ์ฒด๊ฐ ๋ค์ด์๋ค. ๊ทธ๋์ files[i] ์ด๋ฐ ์์ผ๋ก i ๋ฒ์งธ์ file ๊ฐ์ฒด์๋ ์ ๊ทผ์ด ๊ฐ๋ฅํ๋ค.
FileList {0: File, length: 1}
๊ทธ๋ผ file ๊ฐ์ฒด
๋ ์ด๋ค ํํ๋ก ์ด๋ฃจ์ด์ ธ์์๊น? ๊ธฐ๋ณธ์ ์ผ๋ก file ๊ฐ์ฒด๋ 4๊ฐ์ง ์์ฑ์ ๊ฐ์ง๋ค. ๋ชจ๋ ์์ฑ์ ์ฝ๊ธฐ์ ์ฉ์ผ๋ก ์ฃผ์ด์ง๋ค.
const file = {
lastModified: 1580961046732,
lastModifiedDate: 'Thu Feb 06 2020 12:50:46 GMT+0900 (๋ํ๋ฏผ๊ตญ ํ์ค์) {}',
name: 'dom.png',
size: 192045,
type: 'image/png',
}
1๏ธโฃ lastModified : ๋ง์ง๋ง ์์ ๋ ์ง numberํ์ ์ผ๋ก ๋ฐํ (์์ ๊ฒฝ์ฐ, ํ์ฌ ์๊ฐ)
2๏ธโฃ lastModifiedDate : ๋ง์ง๋ง ์์ ๋ ์ง Date๊ฐ์ฒดํ์
์ผ๋ก ๋ฐํ (Deprecated)
Date ๊ฐ์ฒดํ์ ์ ์ด์ฉํ๊ณ ์ถ์ผ๋ฉด, new Date(lastModified) ๋ฐฉ์์ผ๋ก ์ฌ์ฉํ๊ธฐ!!
3๏ธโฃ name : ๋จ์ ํ์ผ์ ์ด๋ฆ stringํ์ ์ผ๋ก ๋ฐํ (๊ฒฝ๋ก๋ ํฌํจํ์ง ์๋๋ค.)
4๏ธโฃ size : 64๋นํธ ์ ์์ ๋ฐ์ดํธ ๋จ์ ํ์ผ์ ํฌ๊ธฐ numberํ์ ์ผ๋ก ๋ฐํ
ํด๋นํ์ผ์ 192KB ํฌ๊ธฐ์์ ์ ์ ์๋ค.
5๏ธโฃ type : ๋ฌธ์์ด์ธ ํ์ผ์ MIME
ํ์
stringํ์
์ผ๋ก ๋ฐํ
MIME ํ์ ์ ํํ๋ type/subtype ์ ๊ตฌ์กฐ๋ฅผ ๊ฐ์ง๋ฉฐ, ๋ค์๊ณผ ๊ฐ์ ํํ๋ก ์ฐ์ธ๋ค.
text/plain
text/html
image/jpeg
image/png
audio/mpeg
video/mp4
...
์์์๋ ์ฌ์ฉ์๊ฐ ์ ๋ ฅํ File๊ฐ์ฒด์ ๋ํ ์ ๋ณด๋ฅผ ์ป๋ ๋ฐฉ๋ฒ์ ๋ํด์ ์์๋ณด์๋ค. ๊ทธ๋ผ File ๊ฐ์ฒด์ ์ค์ ๋ฐ์ดํฐ์๋ ์ด๋ป๊ฒ ์ ๊ทผํ ๊น?
๋จผ์ file ๊ฐ์ฒด URL์ ์์ฑํด์ ํด๋น file์ ์ ๊ทผํ๋ ๋ฐฉ๋ฒ์ด ์๋ค. ์ด ๋ฐฉ๋ฒ์ window ๋ธ๋ผ์ฐ์ ์ ์ญ๊ฐ์ฒด์ URL ์์ฑ์ ๋ฉ์๋๋ฅผ ์ฌ์ฉํ๋ฉด ๋๋ค.
const img = document.createElement('img')
img.src = window.URL.createObjectURL(files[0])
img.onload = function() {
window.URL.revokeObjectURL(this.src)
}
1๏ธโฃ createObjectURL() : ์ธ์๋ก File๊ฐ์ฒด๋ฅผ ๋ฐ์ผ๋ฉฐ, ํด๋น file์ ๊ณ ์ URL ์ ๋ณด ์์ฑํ๊ณ ๋ฐํํ๋ค.
2๏ธโฃ revokeObjectURL() : file์ด ๋ก๋๊ฐ ์๋ฃ๋์์ ์, ๋์ด์ URL์ ๋ณด๋ ์ฌ์ฉ๋์ง ์๊ธฐ ๋๋ฌธ์ ๋ฉ๋ชจ๋ฆฌ ๋์๋ฅผ ๋ง๊ธฐ ์ํด ํด์ฒดํด์ฃผ์ด์ผ ํ๋ค.
URL๋ก ์ ๊ทผ ํ๋ ๊ฒฝ์ฐ, ์ฃผ์ ํน์ง์ผ๋ก๋
๐. URL์ ์์ฑํ๋ ๊ฒ์ด๊ธฐ ๋๋ฌธ์ ๋๊ธฐ์ ์ผ๋ก ์คํ๋๋ฉฐ ์๊ฐ์ด ๋น ๋ฅด๋ค.
๐. ํ์ง๋ง URL์ด ์ฌ์ฉ๋์์ผ๋ฉด ์๋์ ์ผ๋ก ํด์ฒดํด์ฃผ์ด์ผํ๋ ๋จ์ ์ด ์๋ค.
๋๋ค๋ฅธ ๋ฐฉ๋ฒ์ FileReader ๊ฐ์ฒด๋ฅผ ์ฌ์ฉํ๋ ๊ฒ์ธ๋ฐ, FileReader ๊ฐ์ฒด๋ ๋น๋๊ธฐ์ ์ผ๋ก file์ ์ฝ๊ณ ์ ์ฅํ๋ ๊ฒ์ ๊ฐ๋ฅํ๊ฒ ํด์ค๋ค.
์์ URL๋ก ์ ๊ทผํ๋ ๋ฐฉ๋ฒ๊ณผ ๋น๊ตํ์ ๋, ์ด๋ฌํ ํน์ง์ ๊ฐ์ง๋ค.
๐. ๋น๋๊ธฐ์ ์ผ๋ก ์ค์ file์ ๋ฐ์ดํฐ๋ฅผ ์ฝ๊ธฐ ๋๋ฌธ์, ์๊ฐ์ด ๋๋ฆฌ๊ณ ๋ฉ๋ชจ๋ฆฌ ์ฌ์ฉ๋๋ ๋๋ค.
๐. ํ์ง๋ง ์๋์ผ๋ก ํด์ฒดํ์ง ์์๋ ๋๋ค.(๊ฐ๋น์ง ์ปฌ๋ ํฐ์ ์ํด ์๋์ผ๋ก ํด์ฒด)
FileReader ๊ฐ์ฒด์ ๋ฉ์๋๋ 5๊ฐ์ง๊ฐ ์กด์ฌํ๋ค.
1๏ธโฃ FileReader.abort() : ํ์ผ ์ฝ๊ธฐ๋ฅผ ์ค๋จํจ.
2๏ธโฃ FileReader.readAsArrayBuffer() : ํ์ผ์ ์ฝ๊ณ , result์์ฑ์ ํ์ผ์ ArrayBuffer ํํ๋ฅผ ์ ์ฅ.
๋ฐ์ดํฐ๋ฅผ ์ผ์ ํ ํฌ๊ธฐ๋ก ์๋ผ ์๋ฒ๋ก ๋ณด๋ผ ๋ ์ฌ์ฉ.
3๏ธโฃ FileReader.readAsBinaryString() : ํ์ผ์ ์ฝ๊ณ , result์์ฑ์ ํ์ผ์ ์์ ์ด์ง ๋ฐ์ดํฐ ํํ๋ฅผ ์ ์ฅ.
4๏ธโฃ FileReader.readAsDataURL() : ํ์ผ์ ์ฝ๊ณ , result์์ฑ์ ํ์ผ์ ๋ํ๋ด๋ URL์ ์ ์ฅ.
5๏ธโฃ FileReader.readAsText() : ํ์ผ์ ์ฝ๊ณ , result์์ฑ์ ํ์ผ์ ํ ์คํธ ๋ฌธ์์ด ํํ๋ฅผ ์ ์ฅ.
๊ทธ๋ฆฌ๊ณ FileReader๋ ๋น๋๊ธฐ ๋งค์ปค๋์ฆ์ ์ฌ์ฉํ๊ธฐ ๋๋ฌธ์, ์ด๋ฒคํธํธ๋ค๋ฌ๋ฅผ ๋ฑ๋กํ์ฌ ์ฌ์ฉํ ์๋ ์๋ค. ์ด 6๊ฐ์ ์ด๋ฒคํธํธ๋ค๋ฌ๋ฅผ ์ฌ์ฉํ ์ ์๋ค.
1๏ธโฃ FileReader.onabort : ์ฝ๊ธฐ ์ค๋จ ์, ํธ๋ฆฌ๊ฑฐ ๋จ.
2๏ธโฃ FileReader.onerror : ์ฝ๋ ๋์ค ์ค๋ฅ ๋ฐ์์, ํธ๋ฆฌ๊ฑฐ ๋จ.
3๏ธโฃ FileReader.onload : ์ฝ๊ธฐ ์๋ฃ ์(์ฑ๊ณต๋ง), ํธ๋ฆฌ๊ฑฐ ๋จ.
4๏ธโฃ FileReader.onloadstart : ์ฝ๊ธฐ ์์ ์, ํธ๋ฆฌ๊ฑฐ ๋จ.
5๏ธโฃ FileReader.onloadend : ์ฝ๊ธฐ ์๋ฃ ์(์ฑ๊ณต,์คํจ), ํธ๋ฆฌ๊ฑฐ ๋จ.
6๏ธโฃ FileReader.onprogress : ์ฝ๋ ๋์ค, ํธ๋ฆฌ๊ฑฐ ๋จ.
์ด๋ฌํ ์ด๋ฒคํธํธ๋ค๋ฌ์ ๋ฉ์๋๋ฅผ ์ด์ฉํด์, ํ์ผ์ ์ค์ ๋ฐ์ดํฐ์ ์ ๊ทผํ๋ ๋ฐฉ๋ฒ์ ๋ค์๊ณผ ๊ฐ๋ค.
const reader = new FileReader()
reader.onload = (event: any) => {
console.log('reader', event.target.result)
}
reader.readAsDataURL(e.target.files[0])
์ฝ๋์ ํ๋ฆ์ ๋ค์๊ณผ ๊ฐ๋ค. โFileReader ๊ฐ์ฒด ์ธ์คํด์ค reader๋ฅผ ์์ฑ > onload ์ด๋ฒคํธ ํธ๋ค๋ฌ ๋ฑ๋ก > readAsDataURL() ๋ฉ์๋๋ฅผ ์ฌ์ฉํด์ ํด๋น ํ์ผ์ ์ฝ์ > onload ์ด๋ฒคํธ ํธ๋ค๋ฌ ํธ๋ฆฌ๊ฑฐ ๋จ > ํด๋น ํ์ผ์ result ์์ฑ๊ฐ ์ถ๋ ฅ
์ค์ ์ด๋ฌํ ๊ฐ์ผ๋ก ์ถ๋ ฅ๋๋ฉฐ, img.src ๊ฐ์ผ๋ก ๋ฃ์ด์ ์ฌ์ฉ๋ ๊ฐ๋ฅํ๋ค. ๋ง์ฐฌ๊ฐ์ง๋ก ์ฌ๋ฆฐ ํ์ผ์ ๋ค์ ๋ค์ด๋ก๋ํ๋ ๊ฒ๋ ์ด๋ฌํ data URL์ ์ด์ฉํ๋ฉด ๋๋ค.
์ง๊ธ๊น์ง File ๊ฐ์ฒด์ ์ฝ๊ธฐ์ ์ฉ ์์ฑ๋ค๊ณผ, URL๋ก ์ ๊ทผ ํน์ FileReader๊ฐ์ฒด ๋ฉ์๋๋ฅผ ์ด์ฉํด์ ์ ๊ทผํ๋ ๋ฐฉ๋ฒ์ ๋ํด์ ์์๋ณด์๋๋ฐ, ๊ทธ๋ผ ์ฌ์ฉ์๋ก๋ถํฐ ์ ๋ ฅ๋ฐ์ file์ ๋ค๋ฅธ ๊ณณ์ผ๋ก ์ ์กํ๋ ค๋ฉด ์ด๋ป๊ฒ ํ ๊น? ์ด๋ด ๋๋ Blob (Binary large object)๊ฐ์ฒด๋ฅผ ์ด์ฉํ๋ฉด ๋๋ค.
์ฌ์ค File ์ธํฐํ์ด์ค๋ ์ฌ์ฉ์์๊ฒ file์ ํํ๋ฅผ ์ ๋ ฅ๊ฐ์ผ๋ก ๋ฐ๊ธฐ ์ํด์ Blob ์ธํฐํ์ด์ค๋ฅผ ํ์ฅํ ๊ฒ์ด๊ณ , ๋ชจ๋ blob ๊ธฐ๋ฅ์ ์์ํ๋ค. ๊ทธ๋์ blob๊ฐ์ฒด๋ file๊ฐ์ฒด์ ๋น์ทํ ํํ๋ฅผ ๊ฐ์ง๋ค.
console.log(e.target.files[0] instanceof File) // true
console.log(e.target.files[0] instanceof Blob) // true
blob์ file๊ฐ์ฒด์ name, lastModified ์์ฑ์ด ์๊ณ size์ type(MIME)์์ฑ๋ง ๊ฐ๋๋ค.
const blob = {
// lastModified: 1580961046732,
// lastModifiedDate: 'Thu Feb 06 2020 12:50:46 GMT+0900 (๋ํ๋ฏผ๊ตญ ํ์ค์) {}',
// name: 'dom.png',
size: 192045,
type: 'image/png',
}
๊ทธ๋ผ ์ด๋ป๊ฒ blob๊ฐ์ฒด๋ฅผ ์์ฑํด์ ๋ค๋ฅธ ๊ณณ์ผ๋ก ์ ์กํ ๊น?
์์์ File๊ฐ์ฒด์ ์ค์ ๋ฐ์ดํฐ์ FileReader.readAsDataURL()๋ฅผ ํตํ์ฌ ์ ๊ทผํ์ ๋, ์ด๋ฐ์์ URL ๊ฐ์ด ์ถ๋ ฅ๋๋ ๊ฒ์ ํ์ธํ ์ ์์๋ค.
์ด๋ฌํ URLํํ๋ฅผ ๊ธฐ๋ฐ์ผ๋ก ์๋ก์ด blob ์ธ์คํด์ค๋ฅผ ์์ฑํด์ ๋ค๋ฅธ ๊ณณ์ผ๋ก ์ ์กํ๋ฉด ๋๋ค.
[์ฝ๋ ์ถ์ฒ : ํ์ค๋ ๋ธ๋ก๊ทธ]
์ฝ๋๊ฐ ๊น๋ํด์ ์ดํดํ๊ธฐ ์ฌ์ ๋ค.
const dataURLToBlob = dataURL => {
const BASE64_MARKER = ';base64,'
// base64๋ก ์ธ์ฝ๋ฉ ๋์ด์์ง ์์ ๊ฒฝ์ฐ
if (dataURL.indexOf(BASE64_MARKER) === -1) {
const parts = dataURL.split(',')
const contentType = parts[0].split(':')[1]
const raw = parts[1]
return new Blob([raw], {
type: contentType,
})
}
// base64๋ก ์ธ์ฝ๋ฉ ๋ ์ด์ง๋ฐ์ดํฐ์ผ ๊ฒฝ์ฐ
const parts = dataURL.split(BASE64_MARKER)
const contentType = parts[0].split(':')[1]
const raw = window.atob(parts[1])
// atob()๋ Base64๋ฅผ ๋์ฝ๋ฉํ๋ ๋ฉ์๋
const rawLength = raw.length
// ๋ถํธ ์๋ 1byte ์ ์ ๋ฐฐ์ด์ ์์ฑ
const uInt8Array = new Uint8Array(rawLength) // ๊ธธ์ด๋ง ์ง์ ๋ ๋ฐฐ์ด
let i = 0
while (i < rawLength) {
uInt8Array[i] = raw.charCodeAt(i)
i++
}
return new Blob([uInt8Array], {
type: contentType,
})
}
์ด๋ฒ ํฌ์คํ ์ผ๋ก ์ฌ์ฉ์๊ฐ ์ ๋ ฅํ file์ ์ ๊ทผํ๋ ๋ฐฉ๋ฒ์ ๋๊ฐ์ง๋ก ์์๋ณด์๊ณ , ๋ค์ ์ฌ์ ์ก๋ ์ ์๋ blob๊ฐ์ฒด๋ก ๋ง๋๋ ๋ฒ๊น์ง ์์๋ณด์๋ค.
ํ๋ก ํธ์๋๋ ์ฌ์ฉ์๋ก ํ์ฌ๊ธ View์์ ๋์ด์ค๋ action๋ค์ ์ฒ๋ฆฌํ๊ธฐ ๋๋ฌธ์, ์ฌ๋ฌ ๋ค์ํ ์ด๋ฒคํธ๊ฐ ๋ฐ์ํ ์ ์๊ณ , ๋ํ ์์ฒญํ๋ ๋ฐ์ดํฐ์ ํํ๋ ๋ค์ํ ์ ์๋ค. ์ด๋ฌํ action์ ์ ์ ํ ์ฒ๋ฆฌํ๋ ๊ฒ๋ ํ๋ก ํธ์๋ ๊ฐ๋ฐ์์ ์ญ๋ ์ค ํ๋๊ฐ ์๋๊น ๋ค์ํ๋ฒ ์๊ฐํด๋ณด์๋ค.
https://developer.mozilla.org/ko/docs/Web/API/File
https://stackoverflow.com/questions/31742072/filereader-vs-window-url-createobjecturl