1. FileReader에서 readAsDataURL() 메서드는 File 타입 객체를 읽어서 Base64로 인코딩된 문자열 형태의 데이터를 제공한다. File 객체는 바이너리 데이터를 Blob으로 저장하며, 이 데이터를 readAsDataURL() 메서드를 통해 문자열로 변환하여 이미지 등의 데이터를 처리할 수 있게 해준다.
// Blob 타입의 파일을 읽어 Base64로 인코딩된 문자열을 반환하는 함수
export const imageFileReaderP = (file: Blob) =>
new Promise<string>((resolve, reject) => {
// FileReader 객체 생성
const fileReader = new FileReader()
// onload는 파일을 성공적으로 읽었을 때 실행되는 이벤트 핸들러
fileReader.onload = (e: ProgressEvent<FileReader>) => {
// 파일을 Base64로 인코딩한 결과를 가져옴
const result = e.target?.result
// 인코딩 결과가 문자열이면 resolve로 반환, 그렇지 않으면 reject로 에러 반환
if (result && typeof result === 'string') resolve(result)
else reject(new Error(`imageFileReaderP: can't read image file`))
}
// Blob 또는 File 객체를 읽어 Base64 인코딩된 문자열로 변환
fileReader.readAsDataURL(file)
})
2.드래그드롭으로 이미지를 넣거나 클릭해서 파일창을 열수 있게 만든코드
base64는 인코딩하는데 시간이 걸리므로 비동기 Promise처리
export default function FileDrop() {
const [imageUrls,setImageUrls] = useState<string[]>([])
const [error, setError] = useState<Error | null>(null)
const [loading,toggleLoading] = useToggle(false)
const inputRef = useRef<HTMLInputElement>(null)
const onDivClick = useCallback(() => inputRef.current?.click(),[])
const makeImageUrls = useCallback((files: File[]) => {
// 유사 배열 객체를 배열로 변환 (Array.from())
// 각 파일을 Base64로 인코딩된 URL로 변환
const promises = Array.from(files).map(imageFileReaderP)
// 로딩 상태 시작
toggleLoading()
// Base64 인코딩은 시간이 걸리므로, 비동기 작업을 Promise로 처리
Promise.all(promises)
// 새로운 URL 배열과 기존 imageUrls 배열을 합침
.then(urls => setImageUrls(imageUrls => [...urls, ...imageUrls]))
// 에러가 발생하면 에러 상태로 설정
.catch(setError)
// 로딩 상태 종료
.finally(toggleLoading)
}, [toggleLoading])
*주석설명 참고
// 여러개의 파일들을 선택할 수 있다.
const onInputChange = useCallback((e:ChangeEvent<HTMLInputElement>) =>{
setError(null)
const files = e.target.files
files && makeImageUrls(Array.from(files))
},[makeImageUrls])
const onDivDragOver = useCallback((e:DragEvent) => e.preventDefault(),[])
// 이미지를 떨어트려 파일을 추가할 수 있게함
const onDivDrop = useCallback((e:DragEvent) => {
e.preventDefault()
setError(null)
const files = e.dataTransfer?.files
files && makeImageUrls(Array.from(files))},[makeImageUrls]
)
map을 사용해 배열을 반복해서 새로운 배열로 반환
const images = useMemo(() => (
imageUrls.map((url,index) => (
<Div key={index} src={url} className='m-2 bg-transparent bg-center bg-no-repeat bg-contain'
width='5rem' height='5rem'
/>
))
),[imageUrls]
)
console.log(images)
return (
<section className="mt-4">
<Title>FileDrop</Title>
{/* 오류가 발생한 경우 오류 메시지를 표시 */}
{error && (
<div className="p-4 mt-4 bg-red-200">
<p className="text-3xl text-red-500 text-bold">{error.message}</p>
</div>
)}
<div onClick={onDivClick} className="w-full mt-4 bg-gray-200 border border-gray-500">
{/* 파일 업로드 중 로딩 상태를 표시 */}
{loading && (
<div className="flex imtes-center justify-center">
<Button className="btn-circle loading"></Button>
</div>
)}
<div onDragOver={onDivDragOver} onDrop={onDivDrop}
className="flex flex-col items-center justify-center h-40 cursor-pointer">
<p className="text-3xl font-bold">drop images or click me</p>
</div>
<input ref={inputRef} onChange={onInputChange} multiple className="hidden" type="file" accept="image/*" />
</div>
<div className="flex flex-wrap justify-center">{images}</div>
</section>
)
}
결과 화면
클릭하면 파일창이 뜬다 이때 여러 이미지를 선택할 수 있고 드래그해서 이미지를 넣을 수 있다.
유사배열객체 Array.from()을 사용해 배열로 변환 (이미지 두개를 넣었다)
base64로 인코딩이 되어있다
참고한 책입니다
https://www.yes24.com/Product/Goods/125567157