Translate

2019년 4월 14일 일요일

[Axios] File Download 방법 (with Vue.js)






서버에서 ByteArray 형식으로 파일을 내려주는 상황에서 아래와 같은 로직으로 파일 다운로드 처리를 했다.
서버에서 헤더의 Content-Disposition 정보에 파일명을 내려줘야 한다.

==================
2019.06.26 추가
IE 브라우저 파일 다운로드 예외처리 기능 추가
==================


API 모듈 파일
1004lucifer
export function requestDownloadApi (requestObj) {
  // _.merge - Lodash 라이브러리
  let reqHeaders = store.state.api.common // 공통적으로 사용하는 헤더정보
  _.merge(reqHeaders, requestObj.headers) // 요청 시 설정한 헤더 override

  return axios({
    url: requestObj.url,
    headers: reqHeaders,
    responseType: 'arraybuffer',
    method: 'post',
    data: requestObj.data || null
  }).then(res => {
    try {
      let blob = new Blob([res.data], { type: res.headers['content-type'] })
      let fileName = getFileName(res.headers['content-disposition'])
      fileName = decodeURI(fileName) // 파일명 디코딩 (프로젝트에 따라 사용여부 옵션)

      if (window.navigator.msSaveOrOpenBlob) { // IE 10+
        window.navigator.msSaveOrOpenBlob(blob, fileName)
      } else { // not IE
        let link = document.createElement('a')
        link.href = window.URL.createObjectURL(blob)
        link.target = '_self'
        if (fileName) link.download = fileName
        link.click()
      }

      requestObj.callback(res.data)
    } catch (e) {
      console.error(e)
    }
  }).catch(res => { // status 200이 아닌경우에도 콜백호출 (프로젝트에 맞게 수정필요)
    requestObj.callback(res.response.data)
  })
}
function getFileName (contentDisposition) {
  let fileName = contentDisposition
    .split(';')
    .filter((ele) => {
      return ele.indexOf('fileName') > -1
    })
    .map((ele) => {
      return ele
        .replace(/"/g, '')
        .split('=')[1]
    })
  return fileName[0] ? fileName[0] : null
}



사용파일 (vue)

requestDownloadApi({
  url: this.fileDownloadUrl, // required
  headers: { // required
    'CUSTOM-HEADER': 'custom-value' // required
  },
  callback: (data) => { // required
    console.log('data: ', data)
    this.$refs.resData2.innerHTML = data
  },
  data: { // optional
    'fileListSeq': 38
  }
})



PS.
CORS이슈로 인해 서버에서 Content-Disposition 헤더정보를 내려줌에도 axios의 Response 객체에 담겨있지 않아 파일명을 읽을 수 없는 이슈가 있다.
res.headers['content-disposition'] 부분에서 데이터를 가지고 오지 못하는경우 아래의 링크를 참조하길 바란다.
링크 - [Axios] Response Headers의 Content-Disposition 항목이 없는 이슈




참조
 - https://javafactory.tistory.com/1582
 - https://gist.github.com/javilobo8/097c30a233786be52070986d8cdb1743
 - https://nehalist.io/downloading-files-from-post-requests/



댓글 8개 :

  1. 위에 사용하신 다운로드로 혹시 이미지파일이나, pdf파일다운도 같이 가능한가요?? bytearray로 넘어온게 이미지 파일로 변환되어서 다운이 되지않아서요..

    답글삭제
    답글
    1. 특정 파일이 다운안될 이유는 없다고 생각해요.
      서버에서 Response 할 때 헤더의 'content-type' 또는 'content-disposition' 부분과 관련이 있지 않을까 싶습니다.

      삭제
  2. 감사합니다 도움 많이 됐어요!

    답글삭제
  3. 감사합니다 큰 도움이 되었습니다

    답글삭제
    답글
    1. 감사합니다.
      큰 도움이 되었다고 하니 기분이 좋네요. ^^

      삭제
  4. 혹시 위코드로 구현후 크롬에서 동작 구현시 여러차례 같은 pc에서 다운로드 받는경우 서버에서 내려받지 않고 남은 버퍼를 다운 받는거 같은데 혹시 원인을아시나요?

    답글삭제
    답글
    1. 남은 버퍼를 다운받는 이야기가..
      서버로 다시 요청하지 않고 브라우저에 남은 캐시를 이용하여 다운받는것을 의미하는건가요??

      삭제