파일 업로드와 다운로드

Gisele·2021년 11월 23일
1

Javascript

목록 보기
13/14
post-thumbnail

프로젝트에서 파일 업로드와 다운로드를 구현하면서 사용한 코드들의 의미를 알아보기 위해 정리한 글입니다.

파일 업로드

flow :
file input -> 파일 선택 -> formdata 객체 만들기 -> multipart로 전송

Input type=file

<input type="file"
       id="avatar" name="avatar"
       accept="image/png, image/jpeg">

file type은 input 요소의 공용 특성 외에도 추가적인 특성을 지원한다.

accept는 선택할 수 있는 파일 유형을 지정할 수 있다. 확장자(.jpg,.pdf,.doc)나 MIME 유형의 문자열을 값으로 입력하면 해당 유형의 파일만 선택할 수 있다.

아래처럼 입력하면 엑셀파일만 선택할 수 있다.
accept='application/vnd.ms-excel,application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'

capture는 이미지 또는 비디오 데이터를 캡처할 때 사용할 방법이고, files는 선택한 파일을 나열하는 FileList다. multipletrue로 지정하면 여러 개의 파일을 선택할 수 있다.

선택한 파일에는 아래와 같이 접근할 수 있고,

const selectedFile = document.getElementById('input').files[0];

onChange event를 이용해 e.target.files[0]로도 접근할 수 있다.

Formdata

FormData 인터페이스는 form 필드와 그 값을 나타내는 일련의 key/value 쌍을 쉽게 생성할 수 있는 방법을 제공한다. 또한 XMLHttpRequest.send() 메서드를 사용해 쉽게 전송할 수 있다.

리액트에서 formData로 파일을 보내는 코드를 아래처럼 짰다.

  const [targetFile, setTargetFile] = useState<RcFile[]>([]);

const handleFileChange = (e)=>{
	const file = e.target.files[0]
    setTargetFile(file)
}

const onSubmit = ()=>{
	const formData = new FormData();
  	
  	formData.append('fileKey',targetFile[0])
  
	  axios.post("api/uploadfile", formData)
      		.then(res => {
        		console.log(res);
      		})
      		.catch(err => {
        		console.log(err);
      		});
}


...
<input type="file" name="file" onChange={e => this.handleFileChange(e)}/>

Multipart

Multipart란 웹 클라이언트가 요청을 보낼 때 http 프로토콜의 바디 부분에 데이터를 여러 부분으로 나눠서 보내는 것을 의미한다. 여러 부분을 하나의 복합 메세지로 보낸다.

form을 통해서 파일을 등록하고 전송할 때 웹 브라우저가 보내는 HTTP 메시지는 Content-Type 속성이 multipart/form-data로 지정되며, 정해진 형식에 따라 메시지를 인코딩하여 전송한다. 이를 처리하기 위해 서버는 멀티파트 메시지에 대해서 각 파트별로 분리하여 개별 파일의 정보를 얻게 된다.

HTTP는 위 이미지와 같이 4개의 파트로 나뉘고
Header의 Content-Type 필드로 message body에 들어가는 데이터 타입을 명시한다.

multipart 메시지는

  • 서로 붙어있는 여러개의 메시지를 포함하여 하나의 복합 메시지로 보내진다.
  • MIME multipart 메세지는 Content-type 헤더에 boundary 파라미터를 포함한다
  • boundary는 메시지 파트를 구분하는 역할을 하며, 메세지의 시작과 끝 부분도 나타낸다
  • boundary를 선택하는 것은 클라이언트의 몫이다. 보통 무작위의 문자(UUID등)을 선택해 메세지의 본문과 충돌을 피한다
  • HTTP form을 채워서 제출하면, 가변 길이 텍스트 필드와 업로드 될 객체는 각각 멀티파트 본문을 구성하는 하나의 파트가 되어 보내진다. 멀티 파트 본문은 여러 다른 종류의 길이의 값으로 채워진 form을 허용한다.
  • multipart/form-data : 사용자가 양식을 작성한 결과 값의 집합을 번들로 만드는데 사용한다.

대용량 업로드에서 난 에러처리

파일 업로드를 진행하던 중

504 Gateway Time-out

에러가 발생했다. 파일 용량이 커서 nginx의 기본 timeout을 넘어갔기 때문에 발생하는 에러였다. 서버와 클라이언트간 connection을 유지하는 시간이 길기 때문이고 nginx의 기본 timout 시간을 바꿔주면 에러는 해결된다.

우리는 서버에서 bull library를 이용해 queue를 만들어 파일부터 전송하고, 업로드 과정은 클라이언트와의 connection과는 상관없이 처리하는 방식으로 구현했다.

파일 다운로드

flow :
api 요청 -> response를 blob으로 받아옴 -> 브라우저에서 다운로드

Blob

Blob객체는 파일류의 불변하는 미가공 데이터를 나타낸다. 텍스트와 이진 데이터의 형태로 읽을 수 있으며, ReadableStream 으로 변환한 후 그 메서드를 사용해 데이터를 처리할 수도 있다.

Javascript에서 File 인터페이스는 사용자 시스템의 파일을 지원하기 위해 Blob 인터페이스를 확장한 것이므로, 모든 Blob 기능을 상속한다.

생성

const blob = new Blob(array,options)

const blob = new Blob([response.data], { type: response.headers['content-type'] });
  • array : ArrayBuffer, ArayBufferView,Blob(file),DOMString 객체 또는 이러한 객체가 혼합된 Array를 사용할 수 있다.

  • option
    - type : 데이터의 MIME 타입, 기본값은 ""
    - endings : \n을 포함하는 문자열 처리를 'transparent'와 'native'로 지정. 기본값은 transparent

Property는 size type를 가지고, slice(start,end,type)를 사용해 지정된 바이트 범위의 데이터를 포함하는 새로운 Blob 객체를 만들 수 있다. 이를 Chunks 객체라고 한다.

Blob URL

  • Blob 객체를 가리키는 URL 생성
  • createObjectURL : Blob 객체를 나타내는 URL을 포함한 다음과 같은 DOMString을 생성. 이 Blob URL은 생성된 window의 document(브라우저)에서만 유요하다
  • revokeObjectURL() : URL.createObjectURL()을 통해 생성한 기존 URL을 폐기한다. revokeObjectURL을 통해 해제하지 않으면 자바스크립트 엔진에서 GC되지 않는다. 메모리 누수를 방지하기 위해 생성된 URL을 DOM과 바인딩한 후에는 해제하는 것이 좋다.
  • revokeObjectURL
export const downloadFile = (response: AxiosResponse<Blob>) => {
  const blob = new Blob([response.data], { type: response.headers['content-type'] });
  const url = window.URL.createObjectURL(blob);
  const link = document.createElement('a');
  link.href = url;

  const filename = response.headers['content-disposition']
    .split('filename=')[1]
    .split(';')[0];

  link.setAttribute('download', filename);
  document.body.appendChild(link);
  link.click();
  link.remove();
  window.URL.revokeObjectURL(url);
};

reference

profile
한약은 거들뿐

0개의 댓글