第十七篇 - 下载文件的两种方式

这篇介绍Excel下载的两种方式。SpringMVC + Vue3

一、使用BLOB文件流下载Excel

参考链接:https://blog.csdn.net/m0_66607230/article/details/129161820

先来看看后端Controller的代码

controller.java

@RequestMapping(value = "/downloadExcel", method = RequestMethod.POST)
    @ResponseBody
    public void DownloadExcel(@ModelAttribute("User") User user, HttpServletRequest request, HttpServletResponse response) throws IOException{
//        String excel_path = request.getParameter("test_excel_path"); // 前端传递模板excel路径
        String excel_path = "D:\\test.xlsx"; // 直接定义模板excel路径进行测试
        Workbook wb = null;
        Sheet sheet = null;
        FileInputStream inputStream = new FileInputStream(excel_path);
        wb = new XSSFWorkbook(inputStream); // 读取模板表格内容
        if (wb != null) {
            try {
                sheet = wb.getSheetAt(18); // 读取第18个sheet,excel表中sheet是从0开始数起
                Row row = sheet.getRow(2);  // 定位excel的第2行,行数也是从0开始数起
                Cell cell1 = row.getCell(2); // 定位excel的第2行第2列,列数也是从0数起
                System.err.println(getStringCellValue(cell1)); // 打印第2行第2列的字符串
                Cell word = row.getCell(3);  // 定位excel的第2行第3列
                word.setCellValue("test words"); // 设置excel的第2行第3列的信息为test words

                response.setContentType("application/vnd.ms-excel;charset=utf-8"); // 设置响应格式,前端要与这保持一致
                response.setHeader("Content-Disposition",
                        "attachment;filename=\"" + new String(("文档名称.xlsx").getBytes("gb2312"), "ISO8859-1"));
                OutputStream output = response.getOutputStream();
                wb.write(output); // 将修改后的表格信息写入response输出
                output.close();
                inputStream.close();
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }
View Code

 

这一段代码就是返回的blob文件流。

再来看看前端Vue如何接收

async downloadExcel() {
      let data_excel = {
        "test_excel_path": this.testexcelpath,
      }
      // 后端保存excel
      await axios
          .post('http://ip:port/project/downloadExcel', qs.stringify(data_excel), {responseType: 'blob'})
          .then(successResponse => {
            console.log(successResponse.data)
            var a = document.createElement('a')
            const blob = new Blob([successResponse.data],{type: 'application/vnd.ms-excel'})
            a.href = URL.createObjectURL(blob)
            a.download = "aaa.xlsx"
            a.style.display = 'none'
            document.body.appendChild(a)
            a.click()
            document.body.removeChild(a)
            console.log("下載成功")
          })
          .catch(failResponse => {
            console.log(failResponse)
          })
}
View Code

 

这样点击下载按钮时,会触发downloadExcel函数,浏览器会把它下载到客户端默认的下载文件夹中

升级一下,使程序可以监测文件是否下载完成的状态。

首先,要修改后台的controller,传递blob数据还得携带content-length请求头,告诉文件大小是多少

 然后,要修改前端vue的axios函数https://www.jianshu.com/p/ef9129945b98?utm_campaign=maleskine&utm_content=note&utm_medium=seo_notes&utm_source=recommendation

 onDownloadProgress就是检测文件传输的进度,event.loaded是当前接收的数据,event.total是总大小。在此函数中不能使用this变量,所以要在函数里定义一个变量等于this

 

二、服务器存在excel本地文件,客户端通过window.open访问URL下载

 先来看看后端Controller的代码

controller.java

@RequestMapping(value = "/downloadExcel", method = RequestMethod.POST)
    @ResponseBody
    public SelfResultVo DownloadExcel(@ModelAttribute("User") User user, HttpServletRequest request) throws IOException {
//        String templatepath = request.getParameter("test_excel_path");
        String templatepath = "D:\\test.xlsx";
        Workbook wb = null;
        Sheet sheet = null;
        String excel_path_tmp = "aaa.xlsx";
        String excel_path = "D:\\" + excel_path_tmp;
        File file = new File(excel_path);
        if (file.exists()){
            file.delete();
        }
        FileInputStream inputStream = new FileInputStream(templatepath);
        wb = new XSSFWorkbook(inputStream);
        if (wb != null) {
            try {
                sheet = wb.getSheetAt(18);
                Row row = sheet.getRow(2);
                Cell cell1 = row.getCell(2);
                System.err.println(getStringCellValue(cell1));
                Cell word = row.getCell(3);
                word.setCellValue("test words");

                FileOutputStream outputStream = new FileOutputStream(excel_path);
                wb.write(outputStream);
                outputStream.close();
                inputStream.close();
            } catch (Exception e) {
                e.printStackTrace();
                return new SelfResultVo(201, "Download fail", "");
            }
        }
        String path = "http://ip:port/files/"+excel_path_tmp;
        String encodeUrl = URLEncoder.encode(path, "UTF-8");
        String encodeUrl1 = encodeUrl.replace("%3A", ":");
        String encodeUrl2 = encodeUrl1.replace("%2F", "/");
        return new SelfResultVo(200, "", encodeUrl2);
    }
View Code

 

这一段代码是服务器将数据写入模板excel,并放在服务器本地,然后直接在浏览器使用URL可以下载服务器的文件,不过需要在IDEA中配置tomcat的deployment添加文件存放的路径。

 这样就可以使用[http://ip:port/files/aaa.xlsx]这个URL从浏览器下载文件了。

当然我们需要的是前端通过按钮下载,那么再来看看前端Vue如何触发下载的。

async downloadExcel() {
      var download_link = ""
      let data_excel = {
        "test_excel_path": this.testexcelpath,
      }
      // 后端保存excel
      await axios
          .post('http://ip:port/project/downloadExcel', qs.stringify(data_excel))
          .then(successResponse => {
            console.log(successResponse.data)
            if (successResponse.data.code !== 200) {
              this.$message.error("Failed! Download failed")
              console.log('下載失敗')
            } else {
              download_link = successResponse.data.data
              console.log('準備下載')
            }
          })
          .catch(failResponse => {
            console.log(failResponse)
            // this.$message.error("Failed! Download failed")
          })
        if(download_link !== ""){
        window.open(download_link, '_self');
        console.log("下載成功")
      }
}
View Code

 

使用Window.open()访问URL目前有个矛盾点:

1. 使用上述代码去下载文件【window.open(url,"_self")】,可以实现在当前页面下载文件,但有个问题是无法监测到文件是否下载完毕,代码在执行完window.open这句话会直接往下走,而不会管文件是否下载完毕。

2. 使用下述代码去监测window.open是否下载完毕的话,会在执行window.open时打开新页面。

let net = window.open(download_link)
net.addEventListener("beforeunload", e => {
console.log("下载结束")
})
所以这种方式UI界面不是很友好。
 
升级一下,使用BLOB下载文件(并非一定要Excel)的方法
参考链接:https://blog.csdn.net/Connie_kinkin/article/details/123298334
后端代码:
@RequestMapping(value = "/download_file", method = RequestMethod.POST)
    @ResponseBody
    public void downLoadFile(HttpServletRequest request, HttpServletResponse response) throws IOException {
        String url_path = request.getParameter("url");
        ServletOutputStream out = null;
        try {
            // 与服务器建立连接
            URL url = new URL(url_path);
            URLConnection conn = url.openConnection();
            InputStream inputStream = conn.getInputStream();
            try {
                //1.设置文件ContentType类型,这样设置,会自动判断下载文件类型
                response.setContentType("multipart/form-data");
            } catch (Exception e){
                e.printStackTrace();
            }
            out = response.getOutputStream();
            // 读取文件流
            int len = 0;
            byte[] buffer = new byte[1024 * 10];
            while ((len = inputStream.read(buffer)) != -1) {
                out.write(buffer, 0, len);
            }
            out.flush();
        } catch (Exception e){
            e.printStackTrace();
        }
    }
View Code

 前端代码:

downfile (url, file_name){
      let data = {
        "url": url
      }
      let fileName = file_name
      this.$axios.post("url/download_file", qs.stringify(data), {responseType: 'blob'})
          .then(successResponse => {
            console.log(successResponse.data)
            var a = document.createElement('a')
            const blob = new Blob([successResponse.data],{type: 'multipart/form-data'})
            a.href = URL.createObjectURL(blob)
            a.download = fileName
            a.style.display = 'none'
            document.body.appendChild(a)
            a.click()
            document.body.removeChild(a)
            console.log("下載成功")

          })
          .catch(failResponse => {
            console.log(failResponse)
          })
    },
View Code

 

OK.

posted @ 2023-09-27 10:49  o云淡风轻o  阅读(267)  评论(0编辑  收藏  举报