File 핸들링 제약
• 브라우저에서 실행되는 프론트 웹 애플리케이션에서 사용자 컴퓨터에 저장되있는 파일을 핸들링하는 것은 일차적으로 금지되어 있다.
유저가 브라우저에서 선택한 파일에 한해서만 자바스크립트로 읽어 들일 수 있고 서버에 업로드가 가능하다.
사용자가 선택한 파일이란 이후에 살펴볼 <input type=”file”>을 통해 유저가 선택한 파일이거나
아니면유저가 드래그&드랍으로 웹 페이지에 추가시킨 파일을 의미한다.
<input type=”file”>
• 사용자가 애플리케이션에 파일을 추가하기 위한 가장 기본적인 방법은 태그를 이용하는 방법이다.
▼ multiple 속성
• multiple 속성이 추가되면 사용자가 파일 선택 다이얼로그에서 여러 파일을 선택할 수 있다.
▼ accept 속성
• accept 속성을 이용해 사용자가 선택할 파일의 타입을 지정할 수 있다.
<div>
<input type="file" id="fileInput1"/>
</div>
<div>
<input type="file" id="fileInput2" multiple/>
</div>
<div>
<input type="file" accept="image/*"/>
<input type="file" accept=".txt, .jpg"/>
</div>
<ul id="results"></ul>
"use strict"
let fileNode1 = document.getElementById('fileInput1')
let fileNode2 = document.getElementById('fileInput2')
let resultNode = document.getElementById('results')
function handleFile(e){
//기존 화면 출력 결과 지움
while(resultNode.firstChild){
resultNode.removeChild(resultNode.firstChild)
}
//유저가 선택한 파일 정보 획득
let files = e.target.files //FileList - 여러개 선택이 가능함
if(files.length !== 0){
for(let file of files){//files 의 객체 갯수 만큼 반복적으로 for loop 실행
//for loop 가 한번씩 실행되면서 files 안의 File 객체가 file 에 대입
let item = document.createElement('li')
item.innerHTML = `file name : ${file.name}, file size : ${file.size}, modified : ${new Date(file.lastModified)}`
resultNode.appendChild(item)
}
}
}
fileNode1.addEventListener('change', handleFile)
fileNode2.addEventListener('change', handleFile)
File
• 파일 이용의 기본은 파일명, 파일의 저장시간 혹은 파일 사이즈 정보를 획득하는 것이다.
이를 위해 제공하는 API 가 File 과 FileList 이며
File 은 사용자가 선택한 파일 하나를 표현하며 File 객체 여러 개가 FileList 에 담겨서 이용되고
사용자가 선택한 파일 정보 획득은 대부분 에서 사용자가 파일을 선택한 순간 이루어지며
이를 위해 change 이벤트를 제공한다.
• 사용자가 파일을 하나 선택하든 여러 개 선택하든 모든 파일은 File 객체로 만들어지면
File 객체들이FileList 에 담겨서 전달되는데 File 객체의 name, size, lastModified 프로퍼티로 파일의 이름, 사이즈, 저장시간을 획득하여 화면에 출력시킨다.
▼ 파일 내용 읽기
• 파일의 내용을 읽어서 화면에 출력시켜야 한다면 FileReader API 를 사용한다.
• FileReader 는 파일 내용을 읽기 위해서 제공되는 API 로 다음의 함수를 제공한다.
- readAsText() : 파일의 내용을 텍스트로 읽음
- readAsDataURL() : 파일의 내용을 읽어 base64 로 인코딩해 반환
- readAsArrayBuffer() : 파일의 내용을 배열 버퍼로 읽음
- readAsBinaryString() : 파일의 내용을 바이너리 문자열로 읽음
• 이미지를 읽어 <img> 태그를 이용해 화면에 출력할 때는 readAsDataURL() 을 사용하며
readAsDataURL() 는 읽은 데이터를 base64로 인코딩하여 반환하는데
이 값을 그대로 <img> 태그의 src 에지정하면 이미지가 출력된다.
또한 readAsArrayBuffer() 는 버퍼 개념을 적용해 작게 나누어 읽을 때 유용하며
스트림으로 서버에 업로드 할 때 유용하다.
• FileReader 로 파일을 비동기로 읽기 때문에 읽은 내용은 이벤트를 등록하여 이벤트 콜백함수를 통해 획득한다.
다음은 FileReader 에서 제공하는 이벤트이다.
- load : 파일 읽기 작업이 완료된 순간의 이벤트
- error : 에러가 발생한 순간의 이벤트
- abort : abort() 함수가 호출되어 읽기 작업이 취소된 순간의 이벤트
- progress : 파일을 읽는 동안 주기적으로 발생하는 이벤트
<body>
<input type="file" id="filepicker" multiple />
<ul id="results"></ul>
<script src="main.js"></script>
</body>
"use strict"
let fileNode = document.getElementById('filepicker')
let resultNode = document.getElementById('results')
fileNode.addEventListener('change', handleFile)
function handleFile(e){
while(resultNode.firstChild){
resultNode.removeChild(resultNode.firstChild)
}
let files = fileNode.files
if(files.length !== 0){
for(let file of files){
//파일의 내용을 읽어서 화면에 출력하고자 한다
//텍스트 파일과 이미지 파일의 읽고 출력하는 코드가 상이해진다
//유저가 선택한 파일의 타입에 따라 다른 코드가 실행되게 함
if(file.type.startsWith('text')){
handleTextFile(file)
}else if(file.type.startsWith('image')){
handleImageFile(file)
}
}
}
}
function handleTextFile(file){
let liNode = document.createElement('li')
let nameNode = document.createElement('h3')
nameNode.innerHTML = file.name
liNode.appendChild(nameNode)
//유저가 선택한 파일을 읽기. File 에서 FileReader
let reader = new FileReader()
reader.onload = function(e){
//파일을 다 읽은 순간..
let pNode = document.createElement('p')
//파일의 엔터(\n) 도 데이터이다. html 에서는 태그에 의해서만 UI 효과
//\n 을 <br>로
//문자열.split('구분자') => 전체 문자열을 구분자로 나누어 배열로 리턴.
//배열.join(구분자) => 배열의 데이터를 구분자로 연결해서 하나의 문자열로 리턴
pNode.innerHTML = e.target.result.split('\n').join('<br/>')
liNode.appendChild(pNode)
resultNode.appendChild(liNode)
}
reader.onerror = function(e){
//읽다가 에러가 발생한 경우
//항상 IO(file, network) 은 에러 가능성
let pNode = document.createElement('p')
pNode.innerHTML = '파일 읽기에 실패 했습니다.'
liNode.appendChild(pNode)
resultNode.appendChild(liNode)
}
reader.readAsText(file)//읽기 시작. 내용은 이벤트 콜백함수
}
function handleImageFile(file){
let liNode = document.createElement('li')
let nameNode = document.createElement('h3')
nameNode.innerHTML = file.name
liNode.appendChild(nameNode)
let reader = new FileReader()
reader.onload = function(e){
let imgNode = document.createElement('img')
imgNode.setAttribute('src', e.target.result)
imgNode.setAttribute('width', '100')
liNode.appendChild(imgNode)
resultNode.appendChild(liNode)
}
reader.onerror = function(e){
//읽다가 에러가 발생한 경우
//항상 IO(file, network) 은 에러 가능성
let pNode = document.createElement('p')
pNode.innerHTML = '파일 읽기에 실패 했습니다.'
liNode.appendChild(pNode)
resultNode.appendChild(liNode)
}
reader.readAsDataURL(file)//base64 로 인코딩된 문자열로 읽는다 ==>base64 문자열을
//<img> 태그로 출력이 가능함
}
File - FormData
• 파일 업로드를 위해 여러가지 방법이 있지만 많이 사용하는 방법 중 하나가 FormData 로 파일을 담아 Ajax통신을 통해 업로드 하는 방법이있다.
• FormData 는 자바스크립트 API 이며 서버에 넘겨야 하는 데이터를 키-값 형식으로 표현하기 위해서 제공되는 API 이며FormData 에 다양한 데이터를 추가할 수 있으며 파일 또한 FormData 에 추가해 서버에 전송할 수 있다.
이렇게 만들어진 FormData 를 Ajax 통신을 통해 서버에 전송한다.
<form id="form" action="#">
<!-- file 이외에 upload 시에 일반 데이터 같이 전송 -->
title <input type="text" name="title" id="title"/><br/>
<input type="file" name="file" multiple id="filepicker"/><br/>
<button id="button">업로드</button>
</form>
"use strict"
let button = document.getElementById('button')
let fileNode = document.getElementById('filepicker')
let titleNode = document.getElementById('title')
async function upload(e) {
//form 의 기본 이벤트 처리되지 않게 함
e.preventDefault()
let title = titleNode.value
let files = fileNode.files
if(files.length !== 0){
//FormData 로 서버에 전송할 데이터를 구성
//File 이외에 일반 데이터도 FormData 에 추가 가능
let formData = new FormData()
for(let file of files){
formData.append('file', file)//'file' 추가된 데이터 식별자
}
formData.append('title', title)
//file upload - post 방식
let resp = await axios.post('http://localhost:8000/upload', formData)
if(resp.data.status === 200){
alert('upload ok')
}
}
}
button.addEventListener('click', upload)
File – drag & drop
• 웹 페이지에 드래그 & 드랍으로 파일을 추가해서 업로드 하는 방법을 이용한다.
• 사용자의 드래그 & 드랍을 위한 태그를 준비한 후 이곳에 드래그 & 드랍 이벤트를 등록하여 이벤트 콜백함수에서 사용자가 추가한 파일 정보를 추출해 업로드를 진행한다.
<body>
<div id="drop_zone" ondrop="dropHandler(event)" ondragover="dragOverHandler(event)">
<p>drop zone</p>
</div>
<script src="main.js"></script>
</body>
</html>
"use strict"
async function upload(files){
if(files.length !== 0){
let formData = new FormData()
for(let file of files){
formData.append('file', file)
}
let resp = await axios.post('http://localhost:8000/upload', formData)
if(resp.data.status === 200){
alert('upload ok')
}
}
}
function dropHandler(e){
//브라우저의 기본 이벤트 처리 금지
e.preventDefault()
//drop 한 파일 정보를 추출해서 upload 함수 호출.
upload(e.dataTransfer.files)
}
function dragOverHandler(e){
e.preventDefault()
}
'BootCamp Review' 카테고리의 다른 글
2024.10.11 (Day 24) - TypeScript (0) | 2024.10.11 |
---|---|
2024.10.08 (Day 22) - WebSocket / Web APIs (0) | 2024.10.08 |
2024.10.07 (Day 21) - Storage / Web APIs (0) | 2024.10.07 |
2024.10.04 (Day 20) - Ajax, Axios / Web APIs (0) | 2024.10.07 |
2024.10.02-04 (Day 19,20) - JavaScript 비동기/ Web APIs (0) | 2024.10.04 |