티스토리 뷰

Blob

Blob 객체는 파일류의 불변하는 미가공 데이터를 나타낸다. 텍스트와 이진 데이터 형태로 읽을 수 있으며, ReadableStream으로 변환해서 스트림 메서드를 사용할 수도 있다.
File이 Blob에 기반한 인터페이스로, Blob 인터페이스를 상속해 확장한 것이다.

File

File 인터페이스는 파일에 대한 정보를 제공하고 자바스크립트가 접근할 수 있는 메서드를 제공한다.

서버에서 파일을 다운로드하려면

  • 단순히 파일을 응답으로 보내주는 것뿐만 아니라, 헤더에 Content-Disposition 필드가 필요하다.
  • Content-Disposition 헤더의 타입에는 inline, attachment 2가지 종류가 있는데, inline은 컨텐츠를 display하는 것이고 attachment는 다운로드를 강제하는 값이다.
  • Content-Disposition 헤더의 파라미터는 추가적인 옵셔널 값이다. 파일 이름에 관한 정보나, 파일의 생성된 날짜 등의 추가 정보를 포함시킬 수 있다.
  • 응답 헤더에는 Content-Disposition: attachment; filename=image.gif 이런 식으로 표기된다. 앞에 타입을 명시하고 그 다음에 추가 파라미터를 적는다.

유저가 클릭해서 다운로드를 일으키려면

  • 만약 어떤 url이 다운로드가 가능한 리소스를 가리키고, 이 url로 브라우저에서 접근하면 다운로드가 가능하다고 하자. 하지만 이게 일반적으로 웹 어플리케이션에서는 사용자가 클릭 등의 행위를 했을 때 인터렉션으로 다운로드가 시작된다. 이런 동작을 어떻게 구현해야할까?

  • a tag에 download 속성을 주면 유저가 해당 a 태그를 클릭했을 때 href 속성에 명시된 파일이 다운로드된다.

  • download 속성에는 값을 줘도 되고 안 줘도 된다. 만약 값을 주게 되면 다운로드될 파일의 이름으로 설정된다. 서버 응답에서 Content-Disposition의 파라미터로 추가적인 정보를 넣어줄 수 있다고 했는데, 이 때 파일명이 내려왔다면 이 부분을 값으로 설정해도 될 것이다.

  • 그러면 이제 href 속성에 다운로드 받을 리소스를 가리키는 주소가 있어야한다. 이 주소는 어떻게 얻을 수 있을까?

  • 서버에서 받은 파일을 가리키는 url을 클라이언트에서 만들어주면 된다.

    // 다운로드를 트리거하고 싶은 곳에 click 이벤트 핸들러로 등록해준다. 
    function handleFileDownload() {
      const response = await fetch('api address');
      const file = await response.blob(); 
      const downloadUrl = window.URL.createObjectURL(file); // 해당 file을 가리키는 url 생성
    
      const anchorElement = document.createElement('a');
      document.body.appendChild(anchorElement);
      anchorElement.download = 'some file'; // a tag에 download 속성을 줘서 클릭할 때 다운로드가 일어날 수 있도록 하기
      anchorElement.href = downloadUrl; // href에 url 달아주기
    
      anchorElement.click(); // 코드 상으로 클릭을 해줘서 다운로드를 트리거
    
      document.body.removeChild(anchorElement); // cleanup - 쓰임을 다한 a 태그 삭제
      window.URL.revokeObjectUrl(downloadUrl); // cleanup - 쓰임을 다한 url 객체 삭제
    }
  • url을 만드는 메서드는 web api 중 하나인 URL.createObjectURL 정적 메서드를 사용하면 된다. 이 메서드는 파라미터로 받은 객체를 가리키는 URL을 DOMstring(UTF-16 문자열)으로 리턴한다.

  • 이렇게 만들어진 url은 해당 document 내에서만 local하게 유효하며, 생성한 창의 document가 닫히면 자동으로 무효화된다.

  • 생성된 url을 해체하려면 URL.revokeObjectURL 을 사용하면 된다. 더 이상 사용할 일이 없을 때 브라우저에게 메모리에서 삭제해도 된다고 알려주는 것이다. 위 예시에서는 다운로드가 시작되고 나면 해당 url은 쓰임을 다한 것이므로 삭제해준다.

  • 이 url을 임시로 생성한 a tag의 href로 지정해주고, download 속성을 준 다음, click을 호출하여 코드 상으로 다운로드를 트리거하고, 쓸모 없어진 a 태그와 url을 메모리에서 삭제해주면 된다.

다운로드가 아닌 새로운 창에서 미리보기로 열고 싶다면

  • 위에서 url을 만드는 작업까지 수행한 다음, window.open(url)이나, download 속성 없이 a 태그에 target=_blank를 줘서 이미지를 디스플레이해도 된다.
  • 단 이때 한가지 이슈가 있었는데, 서버로부터 받아온 blob의 type이 application/octect-stream인 경우에는 파일이 미리보기로 오픈되지 않고 무조건 다운로드가 진행되는 이슈였다. 이 MIME type은 텍스트가 아닌 모든 컨텐츠의 기본 값이라고 할 수 있다. 즉, 제대로 타입이 지정되지 않은, typescript 언어로 비유하자면 any같은 녀석인 셈이다. 서버에서 전송하는 MIME type을 제대로 지정하는 것이 필요한 이유는 브라우저가 전송받은 것이 어떤 타입인지 알고 그에 해당하는 기본 동작을 설정하기 위해서이다. 반면 application/octect-stream은 쓸모 있는 정보를 주지 못하는 타입이기 때문에, image/jpeg나 application/pdf 등의 파일은 문제없이 새 창 미리보기로 열렸던 반면에, 이 타입은 기본 동작을 수행하지 못하고 무조건 다운로드로 진행된게 아닌가 싶다.

REF

'공부일지(TIL) > Web' 카테고리의 다른 글

[Web] JWT  (0) 2022.08.10
[SEO] Canonical tag  (0) 2022.08.09
Static Rendering vs Server Side Rendering  (0) 2021.11.15
package.json의 resolutions  (0) 2021.10.14
Yarn workspaces  (0) 2021.07.28
댓글
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
«   2024/05   »
1 2 3 4
5 6 7 8 9 10 11
12 13 14 15 16 17 18
19 20 21 22 23 24 25
26 27 28 29 30 31
글 보관함