第十七篇 - 下载文件的两种方式
这篇介绍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(); } } }
这一段代码就是返回的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) }) }
这样点击下载按钮时,会触发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); }
这一段代码是服务器将数据写入模板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("下載成功") } }
使用Window.open()访问URL目前有个矛盾点:
1. 使用上述代码去下载文件【window.open(url,"_self")】,可以实现在当前页面下载文件,但有个问题是无法监测到文件是否下载完毕,代码在执行完window.open这句话会直接往下走,而不会管文件是否下载完毕。
2. 使用下述代码去监测window.open是否下载完毕的话,会在执行window.open时打开新页面。
net.addEventListener("beforeunload", e => {
console.log("下载结束")
})
@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(); } }
前端代码:
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)
})
},
OK.