[Nuxt3] contentDisposition에서 FileName 추출하기

devMini·2024년 10월 17일
0

회사에서 사용자가 첨부한 파일을 다운로드 하는 기능을 만들어야했습니다.

이전에는 base64로 내려줘서 프론트에서 디코딩해서 다운로드하게끔 했었지만
이번에는 respons의 getOutpuStream을 이용하여 바이트배열을 출력하도록하여 이것을 프론트에서 핸들링하도록 하였습니다.

그런데 처음으로 제대로 된 파일 다운로드 기능을 구현하려다보니
시간이 꽤나 많이 걸렸네요ㅠㅠ

[Content-Disposition]이라는 헤더에 값을 처음 지정해주기도 했고
이를 프론트에서 추출하는 것도 처음이어서 그랬던 것 같습니다.


프론트 코드를 보기전에
백엔드에서 어떻게 내려보내주는지를 먼저 볼까요?

보편적으로 Content-Disposition 헤더를 파일 다운로드 관련으로 설정해야한다면 "attachment; filename*=UTF-8''" fileName"
의 양식으로 설정합니다.

백엔드 코드 일부

// 한글 등의 문자 깨짐 방지를 위해 파일명을 인코딩
String encodedFileName = URLEncoder.encode(decodeFileName, "UTF-8");

// 공백이 "+"가 되는것을 방지 하기 위해 replace
String replaceEncodedFileName = encodedFileName.replaceAll("\\+", "%20");

response.setHeader("Content-Disposition", "attachment; filename*=UTF-8''" + replaceEncodedFileName);

이 Content-Disposition이 어떤 형식으로 설정되어서 내려오는지를 먼저 안다면 프론트에서 보다 쉽게 파일명을 추출하기 수월할 것입니다.

전 이걸 몰라서 한참 헤맸네요..


프론트 코드 전체

// $get은 nuxt3의 $fetch를 plugin에서 커스텀한 것입니다.
// request url 뒤로 차례대로 params, responseType 입니다.


const downloadResponse = await $get<Blob>('/attchments' , {fileName : 'mini.pdf'} , 'blob');

if(downloadResponse !== 200 || !downloadResponse._data) {
  // 에러 발생시 catch로 throw하여 에러 메세지 처리를 해야하기에
  // 백엔드에서 내려보내준 응답 본문을 그대로 throw한다.
  // 이 응답에는 message,status,code 등이 포함되어있음
  // 이를 ctach 블록에서 JSON.parse를 이용하여 JSON 본문으로 파싱하여 사용하면됨
  throw downloadResponse._data
}

// !! Content-Disposition 헤더에서 값을 추출한다.
const contentDisposition = getTestReportResponse.headers.get('Content-Disposition');

// filename*= 을 기준으로 문자열을 분리해 배열로 만들고, 이를 구조분해할당하여 1번 인덱스의 값만 가져온다.
const [, encodedFileNamePart] = contentDisposition.split('filename*=');

// UTF-8 문자를 제거한다.
const replaceUtf8 = encodedFileNamePart.replace(/UTF-8/g, '');

// 작음따옴표를 제거한다.
const replaceSingleQuote = replaceUtf8.replace(/''/g, '');

// 인코딩 된 fileName을 디코딩한다.
const decodedFileName = decodeURIComponent(replaceSingleQuote);

 // 파일 다운로드
 const downloadUrl = window.URL.createObjectURL(new Blob([downloadResponse._data]));
 const a = document.createElement('a');
 a.href = downloadUrl;
 a.download = decodedFileName;
 a.click();
 a.remove();

생각보다 시간이 많이걸렸지만 파일 다운로드에 관해 많은걸 배울 수 있었습니다.

이 Content-Disposition 헤더에서 파일명을 추출하는 방법은 다양합니다. 찾아보니 정말 다양한 방식으로 추출하더라구요

그 중 match를 사용하는 방법도 보았는데 좋은 방법인 것 같아요.

하지만 젤 좋은 코드는 짧고 가독성 좋으면서 누구든 금방 알 수 있는 코드라고 생각하여(사실 내가 쉽게 할려고) 가장 간단한 방법으로 추출해냈습니다.

추가로 파일 다운로드 중 에러가 발생했을때 어떤식으로 catch에서 핸들링하는지도 몰라 헤맸는데, 이는 다음 포스팅에 적어볼까합니다.

감사합니다.

즐코!

profile
Slowly But Steadily

0개의 댓글