π λ€μ΄κ°λ©°
νλ‘μ νΈ λμ€ HTML Input μ λνν μ»΄ν¬λνΈλ₯Ό ν΅ν΄μ
JavaScript μ File κ°μ²΄λ₯Ό λ€λ€λ³Ό κΈ°νκ° μκ²Όμ΅λλ€.
File API
μ λν λͺ¨λ κ²μ μ΄λ² ν¬μ€νΈμμ λ€λ£¨μ§λ μμ§λ§,
νμν κΈ°λ₯μ ꡬννκΈ° μν΄μ μμλ³Έ λ΄μ©λ€μ μ 리ν΄λ³΄κ² μ΅λλ€.
π File API λ?
File API
λ νμΌμ λν μ 보λ₯Ό μ 곡νκ³ JavaScript λ₯Ό μ΄μ©ν΄μ
νμΌκ°μ²΄μ μ κ·Όν μ μλ λ€μν μΈν°νμ΄μ€λ₯Ό μ 곡ν©λλ€.
μ λ‘λ ν νμΌμ μ΄λ¦μ λ³κ²½νκ³ μμ νλ μ»΄ν¬λνΈλ₯Ό λ§λ€μ΄λ³΄λ©΄μ
File API
μ νμ© λ°©λ²μ λν΄μ μμλ³΄κ² μ΅λλ€.
λ¨Όμ νμΌμ λ€λ£¨λ FileInput
μ»΄ν¬λνΈλ₯Ό λ€μκ³Ό κ°μ΄ μ μνκ² μ΅λλ€.
μ¬λ¬κ°μ νμΌμ μ
λ‘λ ν μ μλλ‘ νκΈ° μν΄ multiple
μμ±μ μΆκ°ν©λλ€.
import React from 'react'
export default function FileInput() {
return <input type="file" multiple />
}
κ·Έλ¦¬κ³ μ΅μμ App
μ»΄ν¬λνΈμμ μ΄λ₯Ό λΆλ¬μ νλ©΄μ κ·Έλ €μ€λλ€.
import React from 'react';
import Input from './Input'
export default function App() {
return (
<div>
<Input />
</div>
);
}
μ΄μ μ¬λ¬κ°μ νμΌμ λμμ μ
λ‘λνλ input
μ»΄ν¬λνΈκ° μΆκ°λμμ΅λλ€!
π μ λ‘λ ν νμΌμ 리μ€νΈ ννλ‘ μκ°ννκΈ°
μ°λ¦¬κ° μνλ건 μ λ‘λ ν κ°κ°μ νμΌλ€μ μ΄λ¦μ λ³κ²½νκ±°λ μμ νλ κ²μ λλ€.
μ΄λ₯Ό μν΄μ μ λ‘λ λ νμΌλ€μ 리μ€νΈ ννλ‘ μκ°ννλ μμ μ΄ νμν©λλ€.
λ¨Όμ FileInput
μ νμΌ κ°μ²΄λ₯Ό κ΄λ¦¬νλ μνκ°μ μΆκ°ν©λλ€.
νμΌ κ°μ²΄λ File
μΈν°νμ΄μ€λ‘ ννλλ©° μ΄λ Blob
μ κΈ°λ°μΌλ‘ ꡬνλμ΄μμ΅λλ€.
κ·Έλ¦¬κ³ File
μΈν°νμ΄μ€μλ name
, lastModified
λ± λ€μν readonly
μμ±μ΄ μ‘΄μ¬νμ¬
νμΌμ λν λ€μν λ©ν μ 보λ€μ κ°μ Έμ¬ μ μμ΅λλ€.
μμλ₯Ό κ°λ¨νκ² νκΈ° μν΄μ, νμΌ μ΄λ¦κ³Ό μμ λ° μ΄λ¦ λ³κ²½ λ²νΌλ§ μΆκ°νλλ‘ νκ² μ΅λλ€.
import React, { useState } from "react";
export default function FileInput() {
const [files, setFiles] = useState<File[]>([]);
const handleChange = (e: React.ChangeEvent<HTMLInputElement>) => {
setFiles(Array.from(e.target.files || []));
};
const handleDelete = () => {
/** TODO */
};
const handleRename = () => {
/** TODO */
};
return (
<div>
<input type="file" multiple onChange={handleChange} />
<ul>
{files.map((file) => (
<li key={file.name}>
{file.name}
<button onClick={handleDelete}>μμ </button>
<button onClick={handleRename}>μ΄λ¦λ³κ²½</button>
</li>
))}
</ul>
</div>
);
}
π νμΌ μμ νκΈ°
handleDelete ꡬννκΈ°
λ¨Όμ νμΌμ μμ νλ νΈλ€λ¬ (handleDelete
) λΆν° ꡬνν΄λ³΄κ² μ΅λλ€.
μ νλ μΈλ±μ€κ°μ μ λ¬ν΄μ slice
μ°μ°μ ν΅ν΄ μλ‘μ΄ νμΌ λ¦¬μ€νΈλ₯Ό μ€μ ν©λλ€.
import React, { useState } from "react";
export default function FileInput() {
const [files, setFiles] = useState<File[]>([]);
const handleChange = (e: React.ChangeEvent<HTMLInputElement>) => {
setFiles(Array.from(e.target.files || []));
};
const handleDelete = (index: number) => {
setFiles([...files.slice(0, index), ...files.slice(index + 1)]);
};
const handleRename = () => {
/** TODO */
};
return (
<div>
<input type="file" multiple onChange={handleChange} />
<ul>
{files.map((file, index) => (
<li key={`${file.name}_${index}`}>
{file.name}
<button onClick={() => handleDelete(index)}>μμ </button>
<button onClick={handleRename}>μ΄λ¦λ³κ²½</button>
</li>
))}
</ul>
</div>
);
}
μ΄μ μ μ½λλ₯Ό μ€μ λ‘ μ€νμμΌλ³΄λ©΄ λ€μκ³Ό κ°μ΄ μμ λμμ΄ μ΄λ£¨μ΄μ§λ κ²μ μ μ μμ΅λλ€.
input.files μ λΆμΌμΉ..? π€
κ·Έλ°λ° μ’ μ΄μν λΆλΆμ΄ μμ§ μμκ°μ?
File
κ°μ²΄λ₯Ό λ΄κ³ μλ μνκ° (files
) λ λ³νκ° λ°μλμ§λ§,
μ€μ input
μμμ files
μλ μ΄ λ³νκ° λ°μλμ§ μμμ
files
μ νμ¬ 3κ°μ νμΌ λͺ©λ‘μ΄ μ μ§λλ κ²μ μ μ μμ΅λλ€.
κ·Έλ¬λ©΄ μ΄ λ¬Έμ λ₯Ό μ΄λ»κ² ν΄κ²°ν μ μμκΉμ?
μ΄λ μλ‘μ΄ FileList
λ₯Ό fileInput.files
μ ν λΉν¨μΌλ‘μ¨ κ°λ₯ν©λλ€.
FileList
μ체λ μμ±μ ν¨μκ° μκΈ° λλ¬Έμ μ§μ μμ±νλ κ²μ λΆκ°λ₯ ν©λλ€.
λμ DataTransfer
μμλ FileList
λ₯Ό μ¬μ©νκΈ° λλ¬Έμ μ΄λ₯Ό ν΅ν΄μ μμ± κ°λ₯ν©λλ€.
DataTransfer μΈν°νμ΄μ€ λͺ μΈ
DataTransfer
λ drag & drop
λμ μ€ λλκ·Έ μ€μΈ μνμ λ°μ΄ν°λ₯Ό μ μ₯νκΈ° μν μΈν°νμ΄μ€μ
λλ€.
ν΄λΉ μΈν°νμ΄μ€κ° μ 곡νλ νλ‘νΌν°λ€μ λ€μκ³Ό κ°μ΅λλ€.
interface DataTransfer {
attribute DOMString dropEffect;
attribute DOMString effectAllowed;
readonly attribute DataTransferItemList items; // β
readonly attribute FileList files; // β
undefined setDragImage(Element image, long x, long y);
DOMString getData(DOMString format);
undefined setData(DOMString format, DOMString data);
undefined clearData(optional DOMString format);
};
μ¬κΈ°μ μ΄λ²μ μ¬μ©ν κ²μ items
μ files
μ
λλ€.
items
λ DataTransferItemList
μΈν°νμ΄μ€λ₯Ό λ°λ₯΄λ©°
ν΄λΉ μΈν°νμ΄μ€μμλ λ€μκ³Ό κ°μ λ©μλλ₯Ό μ 곡ν©λλ€.
items.remove(index)
-> drag data store μμ index λ²μ§Έμ ν΄λΉνλ λ°μ΄ν°λ₯Ό μμ ν©λλ€.
items.clear()
-> drag data store μμ λͺ¨λ λ°μ΄ν°λ₯Ό μμ ν©λλ€.
items.add(data)
items.add(data, type)
-> drag data store μ μλ‘μ΄ λ°μ΄ν°λ₯Ό μΆκ°ν©λλ€.
DataTransfer λ₯Ό νμ©ν΄μ νμΌ μμ ꡬννκΈ°
μ΄μ μμμ μ΄ν΄λ³Έ λ΄μ©μ λ°νμΌλ‘ μ€μ λ‘ νμΌ μμ λ₯Ό ꡬνν΄λ³΄κ² μ΅λλ€.
λ¨Όμ input
νκ·Έμ μ κ·ΌνκΈ° μν ref
κ°μ²΄λ₯Ό μ μΈν΄μ€λλ€.
import React, { useState, useRef } from "react";
export default function FileInput() {
const inputRef = useRef<HTMLInputElement>(null); // β
input tag μ κ·Όμ μν ref
/** no diff */
return (
<div>
<input ref={inputRef} type="file" multiple onChange={handleChange} />
/** no diff */
</div>
)
}
μ΄ν FileList
λ₯Ό μ μ₯ν DataTransfer
κ°μ²΄λ₯Ό μμ±νκ³ ,
μμ ν index
λ₯Ό μ μΈν λλ¨Έμ§ File
λ€μ drag data store
μ μΆκ°ν΄μ€λλ€.
κ·Έλ¦¬κ³ μλ‘μ΄ FileList
λ‘ input.files
λ₯Ό κ΅μ²΄ν©λλ€.
const handleDelete = (index: number) => {
const newFiles = [...files.slice(0, index), ...files.slice(index + 1)];
const store = new DataTransfer();
newFiles.forEach((file) => store.items.add(file));
if (inputRef.current) {
inputRef.current.files = store.files; // β
μλ‘μ΄ FileList λ‘ κ΅μ²΄ν©λλ€.
}
setFiles(newFiles);
};
μ΄μ μμ ν λλ§λ€ μλ‘μ΄ FileList
κ° μμ±λμ΄ input.files
λ ν¨κ» μμ μ²λ¦¬κ° λ©λλ€!
π νμΌ μ΄λ¦ λ³κ²½νκΈ°
νμΌμ μμ ν λμ λ§μ°¬κ°μ§λ‘ νμΌ μ΄λ¦μ λ³κ²½νλ νΈλ€λ¬ (handleRename
) μμ
μλ‘μ΄ FileList
λ₯Ό ν λΉνλ λ°©λ²μ μ¬μ©νλ©΄ λ©λλ€.
λ€λ§ μμ μ΄ν΄λ³Έλλ‘ File
κ°μ²΄μ name
μμ±μ μ½κΈ°μ μ©μ΄κΈ° λλ¬Έμ,
λ€μκ³Ό κ°μ΄ μ§μ μ κ·Όμ ν΅ν΄ μ΄λ¦μ λ³κ²½ν μ μμ΅λλ€.
files[0].name = 'new name' // β
λ°λΌμ μ΄ λμ μ λ³κ²½ν νμΌ μ΄λ¦μΌλ‘ μλ‘μ΄ File
κ°μ²΄λ₯Ό λ§λλ λ°©λ²μ μ¬μ©ν©λλ€.
File κ°μ²΄ μμ±νκΈ°
File
κ°μ²΄λ File μμ±μ ν¨μ
λ₯Ό μ΄μ©ν΄μ λ§λ€ μ μμ΅λλ€.
const newFile = new File([oldFile], 'new_name.png', {type: oldFile.type});
λ§μ½ IE
μ κ°μ΄ File API
λ₯Ό μλ²½νκ² μ§μνμ§ μλ νκ²½μ΄λΌλ©΄
File
μ κΈ°μ ν΄λμ€μΈ Blob
μ μ΄μ©νλ λ°©λ²μ΄ μμ΅λλ€.
File
ν΄λμ€λ κ²°κ΅ Blob
μ νμ₯ν μΈν°νμ΄μ€μ΄κΈ° λλ¬Έμ λ€μκ³Ό κ°μ΄
Blob
μλ μ‘΄μ¬νμ§ μλ name
κ³Ό lastModified
μμ±μ μΆκ°ν΄μ€μΌλ‘μ¨
File
κ°μ²΄μ λμΌνκ² λ€λ£° μ μμ΅λλ€.
function createFileObject(
bits: Blob[],
name: string,
options?: BlobPropertyBag,
) {
try {
return new File(bits, name, options)
} catch (err) {
/** IE λ File API λ₯Ό μ§μνμ§ μκΈ° λλ¬Έμ Blob μ μ¬μ©ν©λλ€. */
const blob: any = new Blob(bits, options || {})
blob.lastModified = Date.now()
blob.name = name
return blob as File
}
}
handleRename ꡬννκΈ°
μ΄μ μ ν¨μλ₯Ό μ΄μ©ν΄μ νμΌ μ΄λ¦μ λ³κ²½νλ νΈλ€λ¬λ₯Ό ꡬνν΄λ³΄κ² μ΅λλ€.
λ³Έλ λͺ©μ μ μ§μ€νκΈ° μν΄μ νμΌ μ΄λ¦μ μ¬μ©μλ‘λΆν° μ λ ₯ λ°λ κ²μ΄ μλλΌ
λλ€ν μ΄λ¦μΌλ‘ μμ±λλλ‘ λ¨μννμ¬ μ§ννκ² μ΅λλ€. π
λ¨Όμ λλ€ν λμλ₯Ό ν΅ν΄ μλ‘μ΄ νμΌ μ΄λ¦μ λ°ννλ ν¨μλ₯Ό ꡬνν©λλ€.
function createRandomFilename() {
return `file_${crypto.getRandomValues(new Uint32Array(1))}`;
}
μ΄μ μλ‘μ΄ File
κ°μ²΄λ₯Ό λ°ννλ μ νΈλ¦¬ν° ν¨μ (createFileObject
) λ₯Ό ν¨κ» μ΄μ©ν΄μ
νμΌ μ΄λ¦μ λ³κ²½νλ νΈλ€λ¬ (handleRename
) μ ꡬννκ² μ΅λλ€.
const handleRename = (index: number) => {
const originFile = files[index];
const newFilename = createRandomFilename();
const newFileObject = createFileObject([originFile], newFilename, {
type: originFile.type
});
const store = new DataTransfer();
const copy = [...files];
copy[index] = newFileObject;
copy.forEach((file) => store.items.add(file));
if (inputRef.current) {
inputRef.current.files = store.files;
}
setFiles(copy);
};
index
μ ν΄λΉνλ νμΌμ μλ‘μ΄ νμΌ κ°μ²΄λ‘ λ°κΎΈμ΄
νμΌ μμ μ λμΌνκ² DataTransfer
λ₯Ό μ΄μ©ν΄ μλ‘μ΄ FileList
λ₯Ό ν λΉν©λλ€.
π CodeSandbox μμ νμΈνκΈ°
μμμ ꡬνν μ»΄ν¬λνΈλ λ€μ λ§ν¬μμ νμΈν΄λ³΄μ€ μ μμ΅λλ€. π
π μ°Έκ³ μλ£
Rename A File With JavaScript In The Browser
Renaming a File() object in JavaScript
File Constructor support in Internet Explorer - wiliammbr's blog
'π¨βπ» web.dev > fe' μΉ΄ν κ³ λ¦¬μ λ€λ₯Έ κΈ
AWS SDK λ‘ s3 νμΌ μ¬μ΄μ¦ μ‘°ννκΈ° (0) | 2022.11.26 |
---|---|
React μ΄λ²€νΈ μ²λ¦¬λ°©μκ³Ό SyntheticEvent (0) | 2022.10.17 |
Next.js API Routes μμ보기 (2) | 2022.07.26 |
React μ»΄ν¬λνΈ(children) νμ΄ννκΈ° (0) | 2022.07.23 |
React μ€λλ ν΄λ‘μ (Stale Closure) μ΄μ ν΄κ²°νκΈ° (0) | 2022.05.11 |
π¬ λκΈ