[React] axios로 서버에서 받은 파일 다운로드 구현
이번에 파일 다운로드 기능을 구현하다가 엄청난 삽질을 하게 돼서 기록 삼아 글을 쓴다.
엑셀 파일이 서버 측에 저장되어 있고, api를 호출하면 서버에서 response로 파일을 보내주고 해당 파일을 다운로드 구현하는 일이었다.
서버 응답은 이런 식으로 샬라샬라 내려옴
구글링을 해보니 responseType을 추가해줘야 한다고 한다.
이리저리 검색해보니 responseType: "arraybuffer"로 넣는 사람도 있고 "blob"을 넣는 사람도 있던데
쓰기/수정 기능을 써야 하는 게 아니라면 불변성이 있는 blob을 권장한다고 한다. 읽기 전용이므로 blob으로 넣었다
그리고 불러온 데이터를 new Blob으로 생성해 준다
파일을 불러올 때 file-saver 라이브러리를 이용하는 방법도 있지만, 나는 라이브러리를 쓰지 않는 방법으로 했다
response 응답받은 이후 blob 데이터 생성해 주고, a 태그를 만들어서 필요한 속성들 추가해 주고 클릭시킨 다음
할 일이 끝나면 링크를 삭제시켰다.
URL.createObjectURL()로 생성한 url을 URL.revokeObjectURL()로 폐기하지 않으면 가비지 콜렉터로 설정되지 않아서 메모리 누수가 있을 수 있다고 한다.
이리저리 수정을 했는데도 자꾸 파일이 손상되었다고 나옴.
하다 하다 파일 이름이 문제인가 싶어서 파일 이름까지 그대로 불러와보기로 함.
서버에서는 response Headers에서 Content-Disposition로 파일 이름을 넘겨주고 있는 상황이었음.
그런데 아무리 response를 찍어봐도 cache-control, content-length, content-type, expires, pragma 같은 정보만 나오고
정작 필요한 Content-Disposition 접근이 안 됐음.
예전에 Authorization 작업할 때도 이런 적이 있어서 감이 딱 왔다. 이건 서버에서 설정해줘야 함!!!
서버에서 Access-Control-Expose-Headers: * 에서 별 외에 따로 명시를 해줘야 한다.
Access-Control-Expose-Headers - HTTP | MDN
The Access-Control-Expose-Headers response header allows a server to indicate which response headers should be made available to scripts running in the browser, in response to a cross-origin request.
developer.mozilla.org
서버에서 설정을 해주면 이렇게 content-disposition에 접근해서 파일 이름을 가져올 수 있게 됨.
content-disposition을 가져와서 쓸데없는 부분은 다 떼주고 파일 이름이 한글 이름이었어서 디코딩까지 시켜주었다.
파일 이름은 서버에서 보내주는 그대로 설정돼서 다운로드는 되나, 자꾸 파일이 손상됐다고 나왔다.
암만 뒤져봐도 도대체 뭐가 잘못됐는지 모르던 찰나 깨달았다.
api 호출할 때 header랑 위치를 바꿔서 호출했다는 사실을 ㅠ 옵션 다음 header를 넣어야 한다. 완전 허무..
그렇게 완성된 코드..
link.click() 때문에 해당 함수가 두 번 호출될 수 있어서 이 부분은 필터를 걸어줘야 한다