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

 

posted @   红尘沙漏  阅读(1376)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 【自荐】一款简洁、开源的在线白板工具 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实现前后端实时推送数据
点击右上角即可分享
微信分享提示