Spring boot + Vue axios 文件下載

後端程式碼:

@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是否有返回數據的攔截處理

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

Exit mobile version