springboot实现文件上传下载
1.用IDEA创建名叫springboot-file的SpringBoot项目,并将Package name 改为com.example.springboot,导入Spring Web和thymeleaf依赖。(如果创建过程中遇到了问题,可以看我写的文章《IDEA中创建SpringBoot项目,并实现HelloWorld》中前三个步骤。)
1 2 3 4 5 6 7 8 | <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-thymeleaf</artifactId> </dependency> |
2.文件上传分为单文件上传和多文件上传,这里通过一个方法实现。在src/main/resources/templates文件下创建upload.html文件。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | <!DOCTYPE html> <html xmlns:th= "http://www.thymeleaf.org" > <head> </head> <body> <form th:action= "@{/upload}" method= "post" enctype= "multipart/form-data" > 选择单个文件<input type= "file" name= "file" ><br><br> 选择多个文件<input type= "file" name= "files" multiple><br><br> <input type= "submit" value= "上传" > </form> </body> </html> |
3.在src/main/resources文件下的application.properties文件中,添加文件保存的路径。
1 | filePath=E:/springboot_save_file/ |
4.在src/main/resources的com.example.springboot文件夹下,创建包controller,并创建FileController类,用于处理所有的文件上传和下载请求。(由于下载页面还没写,所以上传文件完成后,会报404错误,但是不影响功能)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 | package com.example.springboot.controller; import cn.hutool.extra.servlet.ServletUtil; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Controller; import org.springframework.ui.Model; import org.springframework.web.bind.annotation.*; import org.springframework.web.multipart.MultipartFile; import javax.servlet.http.HttpServletResponse; import java.io.File; import java.io.FileInputStream; import java.io.IOException; import java.io.OutputStream; import java.nio.CharBuffer; import java.nio.charset.Charset; import java.nio.charset.CharsetEncoder; import java.util.List; @Controller public class FileController { //读取application.properties文件中的filePath属性 @Value ( "${filePath}" ) private String filePath; /** * 前往上传页面 * @return 页面名称 */ @GetMapping ({ "/upload" , "" }) public String goIndex() { return "upload" ; } /** * 将文件保存到指定文件夹 * @param file 单个文件 * @param files 多个文件 * @return 重定向到controller层中前往下载页面的url * @throws IOException */ @PostMapping ( "/upload" ) public String uploadAndGoDownLoad( @RequestPart ( "file" ) MultipartFile file, @RequestPart ( "files" ) List<MultipartFile> files) throws IOException { //判断文件夹是否存在,不存在时,创建文件夹 File directoryFile = new File(filePath); if (!directoryFile.exists()) { //创建多个文件夹 directoryFile.mkdirs(); } //判断文件是否为空,不为空时,保存文件 if (!file.isEmpty()) { saveFile(file); } //判断上传文件个数是否为0 if (files.size() > 0 ) { for (MultipartFile multipartFile : files) { if (!multipartFile.isEmpty()) { saveFile(multipartFile); } } } return "redirect:/goDownload" ; } /** * 保存所有的所有上传的文件名称,前往下载页面 * @param model * @return 页面名称 */ @GetMapping ( "/goDownload" ) public String goDownload(Model model) { File file = new File(filePath); //判断文件夹是否存在 if (file.exists()) { //获取文件夹下面的所有名称 String[] list = file.list(); model.addAttribute( "fileNames" , list); } return "download" ; } /** * 保存文件到指定位置 * @param file 需要上传的文件 * @throws IOException */ public void saveFile(MultipartFile file) throws IOException { //获取文件名 String name = file.getOriginalFilename(); file.transferTo( new File(filePath + name)); } } |
注意:
1.前端向后端传递的form表单enctype属性的值必须"multipart/form-data。
2.前后端请求方式要一致,且必须为post。
3.@RequestPart注解最好加上,但是不加也可以。注意前后端参数的绑定,对于注解的使用,可以查看我写的《SpringMVC中Controller层常用注解》。
结果:
5.对于文件上传功能,相对较为简单,这里就不具体说了。下面会具体介绍文件下载,前后端实现方式和注意的地方。文件下载和文件上传是连在一起的,都写在了FileController类中。首先前端写采用最简单的方式,实现文件下载,将后端实现方式讲完后,再讲其他前端实现方式。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | <!DOCTYPE html> <html xmlns:th= "http://www.thymeleaf.org" > <head> <script> function downloadFileByOpen(fileName) { window.open( "http://localhost:8080/download/hutool?fileName=" + fileName); } </script> </head> <body> <h3>后端hutool + 前端open方式</h3> <div th: if = "${fileNames} == null" >没有文件可下载</div> <ul> <li th:each= "fileName : ${fileNames}" th:text= "${fileName} + ' 下载'" th:onclick= "downloadFileByOpen([[${fileName}]])" > </li> </ul> </body> </html> |
6.后端实现文件下载方式一:hutool方式。在pom.xml文件中导入hutool-extra的依赖,并设置CharacterEncoding为UTF-8。如果不设置CharacterEncoding,会出现中文文件名乱码。
1 2 3 4 5 | <dependency> <groupId>cn.hutool</groupId> <artifactId>hutool-extra</artifactId> <version> 5.8 . 8 </version> </dependency> |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 | package com.example.springboot.controller; import cn.hutool.extra.servlet.ServletUtil; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Controller; import org.springframework.ui.Model; import org.springframework.web.bind.annotation.*; import org.springframework.web.multipart.MultipartFile; import javax.servlet.http.HttpServletResponse; import java.io.File; import java.io.FileInputStream; import java.io.IOException; import java.io.OutputStream; import java.nio.CharBuffer; import java.nio.charset.Charset; import java.nio.charset.CharsetEncoder; import java.util.List; @Controller public class FileController { //读取application.properties文件中的filePath属性 @Value ( "${filePath}" ) private String filePath; /** * 前往上传页面 * @return 页面名称 */ @GetMapping ({ "/upload" , "" }) public String goIndex() { return "upload" ; } /** * 将文件保存到指定文件夹 * @param file 单个文件 * @param files 多个文件 * @return 重定向到controller层中前往下载页面的url * @throws IOException */ @PostMapping ( "/upload" ) public String uploadAndGoDownLoad( @RequestPart ( "file" ) MultipartFile file, @RequestPart ( "files" ) List<MultipartFile> files) throws IOException { //判断文件夹是否存在,不存在时,创建文件夹 File directoryFile = new File(filePath); if (!directoryFile.exists()) { //创建多个文件夹 directoryFile.mkdirs(); } //判断文件是否为空,不为空时,保存文件 if (!file.isEmpty()) { saveFile(file); } //判断上传文件个数是否为0 if (files.size() > 0 ) { for (MultipartFile multipartFile : files) { if (!multipartFile.isEmpty()) { saveFile(multipartFile); } } } return "redirect:/goDownload" ; } /** * 保存所有的所有上传的文件名称,前往下载页面 * @param model * @return 页面名称 */ @GetMapping ( "/goDownload" ) public String goDownload(Model model) { File file = new File(filePath); //判断文件夹是否存在 if (file.exists()) { //获取文件夹下面的所有名称 String[] list = file.list(); model.addAttribute( "fileNames" , list); } return "download" ; } /** * 使用Hutool实现文件下载 * @param fileName 要下载的文件名 * @param response */ @GetMapping ( "/download/hutool" ) @ResponseBody public void downloadByHutool( @RequestParam (value = "fileName" ) String fileName, HttpServletResponse response) { //防止中文乱码 response.setCharacterEncoding( "UTF-8" ); ServletUtil.write(response, new File(filePath + fileName)); } /** * 保存文件到指定位置 * @param file 需要上传的文件 * @throws IOException */ public void saveFile(MultipartFile file) throws IOException { //获取文件名 String name = file.getOriginalFilename(); file.transferTo( new File(filePath + name)); } } |
7.后端实现方式二:通过查看Hutool源码,自己模仿着源码写的一种方式。将前端download.html文件中,添加方式二的测试。后端添加方式二的实现代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 | <!DOCTYPE html> <html xmlns:th= "http://www.thymeleaf.org" > <head> <script> function downloadFileByOpen(fileName) { window.open( "http://localhost:8080/download/hutool?fileName=" + fileName); } function downloadFileByOpenAndSelf(fileName) { window.open( "http://localhost:8080/download/hutool/self?fileName=" + fileName); } </script> </head> <body> <h3>后端hutool + 前端open方式</h3> <div th: if = "${fileNames} == null" >没有文件可下载</div> <ul> <li th:each= "fileName : ${fileNames}" th:text= "${fileName} + ' 下载'" th:onclick= "downloadFileByOpen([[${fileName}]])" > </li> </ul> <br> <br> <h3>后端模仿hutool + 前端open方式</h3> <div th: if = "${fileNames} == null" >没有文件可下载</div> <ul> <li th:each= "fileName : ${fileNames}" th:text= "${fileName} + ' 下载'" th:onclick= "downloadFileByOpenAndSelf([[${fileName}]])" > </li> </ul> </body> </html> |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 | package com.example.springboot.controller; import cn.hutool.extra.servlet.ServletUtil; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Controller; import org.springframework.ui.Model; import org.springframework.web.bind.annotation.*; import org.springframework.web.multipart.MultipartFile; import javax.servlet.http.HttpServletResponse; import java.io.File; import java.io.FileInputStream; import java.io.IOException; import java.io.OutputStream; import java.nio.CharBuffer; import java.nio.charset.Charset; import java.nio.charset.CharsetEncoder; import java.util.List; @Controller public class FileController { //读取application.properties文件中的filePath属性 @Value ( "${filePath}" ) private String filePath; /** * 前往上传页面 * @return 页面名称 */ @GetMapping ({ "/upload" , "" }) public String goIndex() { return "upload" ; } /** * 将文件保存到指定文件夹 * @param file 单个文件 * @param files 多个文件 * @return 重定向到controller层中前往下载页面的url * @throws IOException */ @PostMapping ( "/upload" ) public String uploadAndGoDownLoad( @RequestPart ( "file" ) MultipartFile file, @RequestPart ( "files" ) List<MultipartFile> files) throws IOException { //判断文件夹是否存在,不存在时,创建文件夹 File directoryFile = new File(filePath); if (!directoryFile.exists()) { //创建多个文件夹 directoryFile.mkdirs(); } //判断文件是否为空,不为空时,保存文件 if (!file.isEmpty()) { saveFile(file); } //判断上传文件个数是否为0 if (files.size() > 0 ) { for (MultipartFile multipartFile : files) { if (!multipartFile.isEmpty()) { saveFile(multipartFile); } } } return "redirect:/goDownload" ; } /** * 保存所有的所有上传的文件名称,前往下载页面 * @param model * @return 页面名称 */ @GetMapping ( "/goDownload" ) public String goDownload(Model model) { File file = new File(filePath); //判断文件夹是否存在 if (file.exists()) { //获取文件夹下面的所有名称 String[] list = file.list(); model.addAttribute( "fileNames" , list); } return "download" ; } /** * 使用Hutool实现文件下载 * @param fileName 要下载的文件名 * @param response */ @GetMapping ( "/download/hutool" ) @ResponseBody public void downloadByHutool( @RequestParam (value = "fileName" ) String fileName, HttpServletResponse response) { //防止中文乱码 response.setCharacterEncoding( "UTF-8" ); ServletUtil.write(response, new File(filePath + fileName)); } /** * 模仿hutool实现文件下载 * @param fileName 要下载的文件名 * @param response * @throws IOException */ @GetMapping ( "/download/hutool/self" ) @ResponseBody public void downloadBySelfAndHutool( @RequestParam (value = "fileName" ) String fileName, HttpServletResponse response) throws IOException { //设置字符编码 response.setCharacterEncoding( "UTF-8" ); //以下模仿hutool进行相应设置 //设置内容类型 response.setHeader( "Content-Type" , "application/octet-stream" ); //设置文件名,是解决中文乱码的关键 response.setHeader( "Content-Disposition" , String.format( "attachment;filename=\"%s\"" , URLEncoder.encode(fileName, "UTF-8" ))); //将文件取出,并写到response FileInputStream fileInputStream = new FileInputStream(filePath + fileName); OutputStream outputStream = response.getOutputStream(); byte [] bytes = new byte [ 1024 ]; int length; while ((length = fileInputStream.read(bytes)) != - 1 ) { outputStream.write(bytes, 0 , length); } fileInputStream.close(); outputStream.flush(); outputStream.close(); } /** * 保存文件到指定位置 * @param file 需要上传的文件 * @throws IOException */ public void saveFile(MultipartFile file) throws IOException { //获取文件名 String name = file.getOriginalFilename(); file.transferTo( new File(filePath + name)); } } |
8.上面两种后端实现文件下载方式,对于前端来说,无论是通过open方式,还是通过ajax方式都可以使用。下面这种是通过返回值方式,这种方式主要用于前后端分离项目中。下面以原生的Ajax,模拟前后端分离项目,介绍前后端的实现方式。
download.html中添加Ajax和返回值方式实现文件下载的测试。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 | <!DOCTYPE html> <html xmlns:th= "http://www.thymeleaf.org" > <head> <script> function downloadFileByOpen(fileName) { window.open( "http://localhost:8080/download/hutool?fileName=" + fileName); } function downloadFileByOpenAndSelf(fileName) { window.open( "http://localhost:8080/download/hutool/self?fileName=" + fileName); } function downloadFileByAjax(fileName) { let xhr = new XMLHttpRequest; xhr.open( "get" , "/download/return?fileName=" + fileName, true ); //发送请求 xhr.send(); xhr.responseType= "blob" ; xhr.onload = function() { if ( this .status === 200 ) { let blob = new Blob([ this .response]); let elink = document.createElement( 'a' ); elink.download = fileName; elink.style.display = 'none' ; elink.href = URL.createObjectURL(blob); document.body.appendChild(elink); elink.click(); // 释放URL 对象 URL.revokeObjectURL(elink.href); document.body.removeChild(elink); } } } </script> </head> <body> <h3>后端hutool + 前端open方式</h3> <div th: if = "${fileNames} == null" >没有文件可下载</div> <ul> <li th:each= "fileName : ${fileNames}" th:text= "${fileName} + ' 下载'" th:onclick= "downloadFileByOpen([[${fileName}]])" > </li> </ul> <br> <br> <h3>后端模仿hutool + 前端open方式</h3> <div th: if = "${fileNames} == null" >没有文件可下载</div> <ul> <li th:each= "fileName : ${fileNames}" th:text= "${fileName} + ' 下载'" th:onclick= "downloadFileByOpenAndSelf([[${fileName}]])" > </li> </ul> <br> <br> <h3>后端返回值 + 前端Ajax方式</h3> <div th: if = "${fileNames} == null" >没有文件可下载</div> <ul> <li th:each= "fileName : ${fileNames}" th:text= "${fileName} + ' 下载'" th:onclick= "downloadFileByAjax([[${fileName}]])" > </li> </ul> </body> </html> |
FileController类中添加返回值方式实现文件上传的代码。(FileController类最终代码)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 | package com.example.springboot.controller; import cn.hutool.extra.servlet.ServletUtil; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Controller; import org.springframework.ui.Model; import org.springframework.web.bind.annotation.*; import org.springframework.web.multipart.MultipartFile; import javax.servlet.http.HttpServletResponse; import java.io.File; import java.io.FileInputStream; import java.io.IOException; import java.io.OutputStream; import java.nio.CharBuffer; import java.nio.charset.Charset; import java.nio.charset.CharsetEncoder; import java.util.List; @Controller public class FileController { //读取application.properties文件中的filePath属性 @Value ( "${filePath}" ) private String filePath; /** * 前往上传页面 * @return 页面名称 */ @GetMapping ({ "/upload" , "" }) public String goIndex() { return "upload" ; } /** * 将文件保存到指定文件夹 * @param file 单个文件 * @param files 多个文件 * @return 重定向到controller层中前往下载页面的url * @throws IOException */ @PostMapping ( "/upload" ) public String uploadAndGoDownLoad( @RequestPart ( "file" ) MultipartFile file, @RequestPart ( "files" ) List<MultipartFile> files) throws IOException { //判断文件夹是否存在,不存在时,创建文件夹 File directoryFile = new File(filePath); if (!directoryFile.exists()) { //创建多个文件夹 directoryFile.mkdirs(); } //判断文件是否为空,不为空时,保存文件 if (!file.isEmpty()) { saveFile(file); } //判断上传文件个数是否为0 if (files.size() > 0 ) { for (MultipartFile multipartFile : files) { if (!multipartFile.isEmpty()) { saveFile(multipartFile); } } } return "redirect:/goDownload" ; } /** * 保存所有的所有上传的文件名称,前往下载页面 * @param model * @return 页面名称 */ @GetMapping ( "/goDownload" ) public String goDownload(Model model) { File file = new File(filePath); //判断文件夹是否存在 if (file.exists()) { //获取文件夹下面的所有名称 String[] list = file.list(); model.addAttribute( "fileNames" , list); } return "download" ; } /** * 使用Hutool实现文件下载 * @param fileName 要下载的文件名 * @param response */ @GetMapping ( "/download/hutool" ) @ResponseBody public void downloadByHutool( @RequestParam (value = "fileName" ) String fileName, HttpServletResponse response) { //防止中文乱码 response.setCharacterEncoding( "UTF-8" ); ServletUtil.write(response, new File(filePath + fileName)); } /** * 模仿hutool实现文件下载 * @param fileName 要下载的文件名 * @param response * @throws IOException */ @GetMapping ( "/download/hutool/self" ) @ResponseBody public void downloadBySelfAndHutool( @RequestParam (value = "fileName" ) String fileName, HttpServletResponse response) throws IOException { //设置字符编码 response.setCharacterEncoding( "UTF-8" ); //以下模仿hutool进行相应设置 //设置内容类型 response.setHeader( "Content-Type" , "application/octet-stream" ); //设置文件名,是解决中文乱码的关键 response.setHeader( "Content-Disposition" , String.format( "attachment;filename=\"%s\"" , URLEncoder.encode(fileName, "UTF-8" ))); //将文件取出,并写到response FileInputStream fileInputStream = new FileInputStream(filePath + fileName); OutputStream outputStream = response.getOutputStream(); byte [] bytes = new byte [ 1024 ]; int length; while ((length = fileInputStream.read(bytes)) != - 1 ) { outputStream.write(bytes, 0 , length); } fileInputStream.close(); outputStream.flush(); outputStream.close(); } /** * 通过返回值方式,实现文件下载 * @param fileName 文件名 * @return 文件流和请求头信息 * @throws IOException */ @GetMapping ( "/download/return" ) @ResponseBody public ResponseEntity<InputStreamResource> download( @RequestParam (value = "fileName" ) String fileName) throws IOException { // 读取文件 String path = filePath + fileName; FileSystemResource file = new FileSystemResource(path); // 设置响应头 HttpHeaders headers = new HttpHeaders(); headers.add( "Content-Disposition" , String.format( "attachment; filename=\"%s\"" , file.getFilename())); return ResponseEntity .ok() .headers(headers) .contentLength(file.contentLength()) .contentType(MediaType.parseMediaType( "application/octet-stream" )) .body( new InputStreamResource(file.getInputStream())); } /** * 保存文件到指定位置 * @param file 需要上传的文件 * @throws IOException */ public void saveFile(MultipartFile file) throws IOException { //获取文件名 String name = file.getOriginalFilename(); file.transferTo( new File(filePath + name)); } } |
注意:
1.前端采用原生的ajax实现的,responseType最好设置为blob(设置为arraybuffer也可以,就是增加转换为blob的步骤)。(开始打算用jQuery中的ajax,但是设置responseType为blob会报错。查找原因,也解决不了。一般这种后端实现方式,对应的是前后端分离项目,然后前端使用的axios。我的能力也有限,知道解决方式的可以评论区留言,一起进步。
报错:jquery.js:10287 Uncaught DOMException: Failed to read the 'responseText' property from 'XMLHttpRequest': The value is only accessible if the object's 'responseType' is '' or 'text' (was 'blob').)
2.查看后端代码可以发现,这种下载文件的方式,我们不用去考虑中文乱码问题了。
9.前端使用axios,后端继续使用返回值方式,实现文件下载功能。以下是download.html最终代码。后端代码没有改变。这种前后端配合方式实现文件下载,在前后端分离项目中应用广泛(尤其在vue+springboot前后端组合中)。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 | <!DOCTYPE html> <html xmlns:th= "http://www.thymeleaf.org" > <head> <!-- 导入axios --> <script src= "https://unpkg.com/axios/dist/axios.min.js" ></script> <script> function downloadFileByOpen(fileName) { window.open( "http://localhost:8080/download/hutool?fileName=" + fileName); } function downloadFileByOpenAndSelf(fileName) { window.open( "http://localhost:8080/download/hutool/self?fileName=" + fileName); } function downloadFileByAjax(fileName) { let xhr = new XMLHttpRequest; xhr.open( "get" , "/download/return?fileName=" + fileName, true ); //发送请求 xhr.send(); xhr.responseType = "blob" ; xhr.onload = function () { if ( this .status === 200 ) { let blob = new Blob([ this .response]); let elink = document.createElement( 'a' ); elink.download = fileName; elink.style.display = 'none' ; elink.href = URL.createObjectURL(blob); document.body.appendChild(elink); elink.click(); // 释放URL 对象 URL.revokeObjectURL(elink.href); document.body.removeChild(elink); } } } function downloadFileByAxios(fileName) { axios({ url: "/download/return" , method: "get" , responseType: "blob" , params: { fileName } }).then(function (res) { console.log(res); let blob = new Blob([res.data]); let elink = document.createElement( 'a' ); elink.download = fileName; elink.style.display = 'none' ; elink.href = URL.createObjectURL(blob); document.body.appendChild(elink); elink.click(); // 释放URL 对象 URL.revokeObjectURL(elink.href); document.body.removeChild(elink); }) } </script> </head> <body> <h3>后端hutool + 前端open方式</h3> <div th: if = "${fileNames} == null" >没有文件可下载</div> <ul> <li th:each= "fileName : ${fileNames}" th:text= "${fileName} + ' 下载'" th:onclick= "downloadFileByOpen([[${fileName}]])" > </li> </ul> <br> <br> <h3>后端模仿hutool + 前端open方式</h3> <div th: if = "${fileNames} == null" >没有文件可下载</div> <ul> <li th:each= "fileName : ${fileNames}" th:text= "${fileName} + ' 下载'" th:onclick= "downloadFileByOpenAndSelf([[${fileName}]])" > </li> </ul> <br> <br> <h3>后端返回值 + 前端Ajax方式</h3> <div th: if = "${fileNames} == null" >没有文件可下载</div> <ul> <li th:each= "fileName : ${fileNames}" th:text= "${fileName} + ' 下载'" th:onclick= "downloadFileByAjax([[${fileName}]])" > </li> </ul> <br> <br> <h3>后端返回值 + 前端axios方式</h3> <div th: if = "${fileNames} == null" >没有文件可下载</div> <ul> <li th:each= "fileName : ${fileNames}" th:text= "${fileName} + ' 下载'" th:onclick= "downloadFileByAxios([[${fileName}]])" > </li> </ul> </body> </html> |
SpringBoot实现文件上传和下载部分,到此结束了。我也是个新手,上面很多内容不够完善,甚至有些是错误的,请大家见谅。这是我在学习过程中做的笔记,感觉对大家可能有所帮助才发出来的,大家可以选择性查看。我也是在不断学习,不断完善自己。如果我在学习过程中,感觉对大家有用的部分,也会再次分享给大家的。谢谢!
上面有个问题还没有解决,也就是,前端怎么通过JQuery中的Ajax实现文件下载。这种方式和原生Ajax区别不大,下面就是代码,只需要设置返回类型为blob即可。(有时候,一个小问题,如果不熟悉,真的很难解决的。。。)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | $.ajax({ type: "get" , url: "/download/return" , data: { fileName }, xhrFields: { responseType: "blob" }, success: function (response) { let blob = new Blob([response]); let elink = document.createElement( 'a' ); elink.download = fileName; elink.style.display = 'none' ; elink.href = URL.createObjectURL(blob); document.body.appendChild(elink); elink.click(); // 释放URL 对象 URL.revokeObjectURL(elink.href); document.body.removeChild(elink); } }); |
原文地址 https://blog.csdn.net/qq_55896432/article/details/127102250?ops_request_misc=&request_id=&biz_id=102&utm_term=springboot%E6%96%87%E4%BB%B6%E4%B8%8A%E4%BC%A0%E4%B8%8B%E8%BD%BD&utm_medium=distribute.pc_search_result.none-task-blog-2~all~sobaiduweb~default-1-127102250.142^v73^control,201^v4^add_ask,239^v2^insert_chatgpt&spm=1018.2226.3001.4187
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 【自荐】一款简洁、开源的在线白板工具 Drawnix
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
· 园子的第一款AI主题卫衣上架——"HELLO! HOW CAN I ASSIST YOU TODAY
· 无需6万激活码!GitHub神秘组织3小时极速复刻Manus,手把手教你使用OpenManus搭建本
2021-02-20 oa流程发起后错误数据删除
2021-02-20 websocket前后端交互
2021-02-20 java代码实现websocket前后端交互
2021-02-20 SpringBoot -- WebSocket实现前后端实时推送数据