Input 태그의 Type을 File로 하여 업로드한다.
value = 파일 경로
c:/fakepath/
르ㄹ 포함하여 숨김accept : 이미지 유형 지정
Image/*
: png 같은 이미지 파일".pdf, .doc, .csv"
등이 가능하다Capture : 휴대폰일 시, 카메라 시작
Multiple : 여러개 이미지 등록가능
FileList: Array-like object로 File들을 가지고 있는 객체이다.
이는 files type의 엘리먼트로 선택된 파일의 리스트에 접근할 수 있다.
드래그 앤 드랍 API 사용할 때 파일의 리스트에도 사용됨 (DataTransfer객체 참조)
var file = document.getElementById('fileItem').files[0]
위 같은 방식으로 파일에 접근할 수 있다.
참조하기 위해서는 for of
혹은 Array.from()
로 변환참조 가능
FIle : 읽기 전용
로컬에서 파일을 선택한 파일은 File로 정의 / FilesList에 저장됨
파일을 선택하면 input, change EventHandler 발생, 선택된 파일은 HTMLInputElement.files에 저장
업로드한 이미지를 미리보기 위해서는 FileReader 객체를 사용한다.
// state
state: {
// 업로드한 파일 객체를 저장할 state
detailImageFile: null,
// 업로드한 파일 객체의 이미지 url을 저장할 state
detailImageUrl: null,
}
// set file reader function : FileReader가 onload되면 FileReader의 result를 반환
// setImageFromFile은 file, setImageUrl을 매개변수로 받는다
const setImageFromFile = ({ file, setImageUrl }) => {
// reader에 빈 FileReader 객체를 선언한다.
let reader = new FileReader();
// FilerReader.onload : load 이벤트의 핸들로.
// 해당 이벤트는 읽기 동작이 성공적으로 완료되었을 때마다 발생
// 비동기여서 원하는 동작을 위해 callback으로 실행해야 한다.
// 읽기 동장이 성공적으로 완료되면 함수를 실행한다
reader.onload = function () {
// setImageUrl 함수는 result키에 reader(FileReader).result를 value 값으로 넣는다
// reader.result = 파일의 내용을 반환한다.
setImageUrl({ result: reader.result });
};
// FileReader의 readAsDataURL메서드 파일 내용 반환이 완료되면 그 파일의 URL을 가져온다
reader.readAsDataURL(file);
};
// 위 함수는 매개변수로 전달되는 file의 url을 받아오고 setImageUrl은 result 키 값에 reader.result를 준다
// render JSX
const fileInputArea =
<>
<input
type="file"
id="detail_image"
accept="image/*"
// 변화가 발생하면 callback 함수가 실행된다
// target(input.file)의 files객체 도달
// 이 사람은 바로 const { files } = event.target 을 이렇게 해버린 것이다
// {target: {files}}
onChange={({ target: { files } }) => {
// 만약 filesr가 있다면 (files의 method는 length 뿐)
if (files.length) {
// 위에서 정의한 SetImageFromFile 함수에서 구조분해를 해서 보냄
// File에 files[0]를
// setImageUrl에 매개변수 result도 구조분해 해버리네..
// 함수 실행하면 setState({detailImageFile: files[0], detailImageUrl: result})한다...
setImageFromFile({
file: files[0],
setImageUrl: ({ result }) => setState({detailImageFile: files[0], detailImageUrl: result});
}
}}
/>
//detailImageFile이 있다면
// src에 변경된 스테이트 값을 넣는다
{detailImageFile && (
<div className="image_area">
<img src={detailImageUrl} alt={detailImageFile.name} />
</div>
)}
</>
// useState 빈배열을 만듬 '선택된 파일들'
const [selectedFiles, setSelectedFiles] = useState([]);
// 이벤트를 매개변수로 받는 함수 정의
const handleImageChange = e => {
// e.target.files가 있는지 찍어봄
console.log(e.target.files);
// 만약 이벤트 타겟의 파일들이 있다면
if (e.target.files) {
// filesArry 변수를 선언한다. 그 변수에는
// e.target.files를 배열로 만듬
// Array.from = 유사배열 / 반복가능 객체를 얕게 복사해 새로운 Array객체로 만드는 것이다
// 그래서 맵을 돌려서 각 배열의 파일이 포함된 배열을 리턴한다.
// CreateObjectURL은 주어진 객체를 가리키는 URL을 DOMString으로 반환
// 해당 URL은 자신을 생성한 창의 document가 사라지면 함께 무효화
// 객체 URL을 해제하기 위해서 revokeObjectURL()을 호출
const filesArray = Array.from(e.target.files).map(file =>
URL.createObjectURL(file)
);
// filesArray에 맵으로 돌린 URL들이 잘 담겨있는지 확인
console.log('filesArray: ', filesArray);
// setSelectedFiles(usestate)로 스테이트 업데이트
// prevImages로 전 이미지들을 얕은 복사해서 가져오고 그 배열에 새롭게 추가된 filesArray를 추가
setSelectedFiles(prevImages => prevImages.concat(filesArray));
// 그리고 다시한번 Array.from(e.target.files).map을 해서 file 객체의 URL을 해제한다.
// 이건 계속 메모리 누수가 발생하는걸 없애기 위해서 하는 것이다.
// 만약 revokeObjectURL을 하지않으면 해당 파일의 URL을 브라우저가 계속 저장한다.
// 하지만 위에서 State에 파일을 concat해줬기 때문에 더이상 브라우저에서 해당 URL을 지니지 않아도 된다.
Array.from(e.target.files).map(
file => URL.revokeObjectURL(file)
);
}
};
// renderPhotos라는 함수를 선언한다.
// 이 함수는 나중에 이미지가 렌더되는 곳에서 state를 받아서 맵으로 돌려서 보여주는 기능을 한다.
const renderPhotos = source => {
console.log('source: ', source);
return source.map(photo => {
return <PreviewImage src={photo} alt="" key={photo} />;
});
};
...
<input onchange={handleImageChange}>
...
{renderPhotos(selectedFiles)}
1차 시도
const onImgChange = async(event: any) => {
setLogoLoading(true);
const formData = new FormData();
formData.append('image', event.target.files[0]);
const response = await apiClient.post('/brand/logo_image', formData);
// reponse.data.location이 업로드한 파일의 url
setLogoLoading(false);
}
2차 시도
const setFile = e => {
const formData = new FormData();
formData.append('image', ImageForUpload);
axios.post('public/data/SellerUpload.json', formData);
};
Delete 버튼 만들기
example
const App = () => { // 새로운 배열 스테이트를 만든다 const [pics, setPics] = useState([]); // 버튼을 클릭 하면 id 값으로 특정하여 가져오게 했다. const removeImage = (id) => { // 새롭게 스테이트를 변경한다. // 매개변수를 주고 매개변수를 필터로 돌려서 아이템을 리턴하는데 id가 맞으면 제외한다. setPics((oldState) => oldState.filter((item) => item.id !== id)); }; useEffect(() => { // 기존에 이미지를 가져오는 함수 setPics(allImages); }, []); return ( <div className="App"> // 맵함수로 데이터 있는데로 돌린다 {pics.map((pic) => { return ( <div style={{ marginBottom: "100px" }}> // 이미지 데이터의 아이디를 보여주고 {pic.id} <img src={pic.imgUrl} width="100px" height="100px" alt="placeholder grey 100px" /> // 버튼을 클릭하면 비동기로 removeImage(pic.id)가 전달되어 작동 <button onClick={() => removeImage(pic.id)}>X</button> </div> ); })} </div> ); }; export default App;