Spring boot + Vue axios 檔案下載

懷瑾於輕發表於2022-01-22

後端程式碼:

@GetMapping("/{sn}")
    @ApiOperation(value = "獲取檔案",notes = "獲取檔案")
    public void downloadFile(@PathVariable String sn, HttpServletResponse response) throws Exception {
        SysFileEntity sysFile = fileService.findBySn(sn);
        if (sysFile != null){
            File file = new File(sysFile.getFilePath());
            if (file.exists()){
                // 設定強制下載不開啟
                response.setContentType("application/force-download");
                // 設定檔名
                response.addHeader("Content-disposition", "attachment;fileName=" + URLEncoder.encode(file.getName(), StandardCharsets.UTF_8));
                response.addHeader("Access-Control-Expose-Headers", "Content-Disposition");
                FileInputStream fis = null;
                BufferedInputStream bis = null;
                byte[] buffer = new byte[1024];
                try {
                    fis = new FileInputStream(file);
                    bis = new BufferedInputStream(fis);
                    OutputStream os = response.getOutputStream();

                    int i = bis.read(buffer);
                    while (i != -1) {
                        os.write(buffer, 0, i);
                        i = bis.read(buffer);
                    }

                }
                finally {
                    if (bis != null) {
                        try {
                            bis.close();
                        } catch (IOException e) {
                            e.printStackTrace();
                        }
                    }
                    if (fis != null) {
                        try {
                            fis.close();
                        } catch (IOException e) {
                            e.printStackTrace();
                        }
                    }
                }
            }
        }

前端程式碼:

//封裝的axios
this.$http.request({
        method: "get",
        url: `${this.$apis.common.file}/${this.ruleForm.fileSN}`,
        data: {},
		//此處可以無視,前端封裝全域性載入遮罩
        loading: false,
        responseType: 'blob'
      }).then(res => {

        const fileName = decodeURIComponent(res.headers["content-disposition"]).split("=")[1];
        const _res = res.data;
        let blob = new Blob([_res]);
        let downloadElement = document.createElement("a");
        //建立下載的連結
        let href = window.URL.createObjectURL(blob);
        downloadElement.href = href;
        //下載後檔名
        downloadElement.download = fileName;
        document.body.appendChild(downloadElement);
        //點選下載
        downloadElement.click();
        //下載完成移除元素
        document.body.removeChild(downloadElement);
        //釋放掉blob物件
        window.URL.revokeObjectURL(href);

      })

常見問題總結

  1. 前端接收到的響應頭中不包含 “content-disposition”,但是在瀏覽器的網路中可以看到,這裡需要後端在響應頭中增加

    response.addHeader("Access-Control-Expose-Headers", "Content-Disposition");
    
  2. 檔名返回到前端後亂碼

    此處需要在後端對檔名轉碼

    //StandardCharsets.UTF_8 等價於"UTF8"
    URLEncoder.encode(file.getName(), StandardCharsets.UTF_8)
    

    在前端檔案解碼

    此處是把整個 “content-disposition”響應頭的內容解碼後再取檔名。也可以先取檔名再解碼

    decodeURIComponent(res.headers["content-disposition"])
    
  3. 注意自己的前端框架裡封裝的axios是否有返回資料的攔截處理

    我這裡因為需要與後端進行身份驗證所以還是用的框架內封裝好的請求方法。但是需要對返回的檔案資料與其他區分出來

image

相關文章