SpringBoot文件上传下载

发布时间 2023-12-04 15:06:16作者: Xproer-松鼠

1、单文件上传
这里的enctype的类型是mulitpart/form-data

两种形式的提交,一种是以form表单的形式,一个是ajax的形式

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>上传文件</title>
<script src="https://cdn.bootcss.com/jquery/3.4.1/jquery.min.js"></script>
</head>
<body>
<!--form表单形式上传-->
<form action="/upload" method="post" enctype="multipart/form-data">
<input type="file" name="multipartFile" id="file" value="上传文件">
<input type="button" value="ajax形式上传" id="btn" onclick="onUpload()">
<input type="submit" value="form表单的submit上传">
</form>

<script>
//var onUpload = function(){}
//ajax形式上传
function onUpload() {
//$("#multipartFile")[0]表示获取到js对象,files[0]表示获取到上传文件的第一个
var file = $("#file")[0].files[0];
var formData = new FormData(); //这个对象是可以让我们模拟form表单的提交
formData.append("multipartFile", file);
formData.append("isOnline", 1);
$.ajax({
type: "POST",
url: "/upload",
data: formData,
processData: false, //这里设置为false表示不让ajax帮我们把文件转换为对象格式,这里必须设置为false
contentType: false, //这里也要设置为false,不设置可能会出问题
success: function (msg) {
alert("上传文件成功");
}
})
}
</script>
</body>
</html>

我们将上传的文件放入到resources下面

这里不能使用request.getServletContext().getRealPath(""),因为这里获取到的路径是tomcat临时文件的目录

springboot中request.getServletContext().getRealPath(“/”)获取的是一个临时文件夹的地址_猿程序plus的博客-CSDN博客_springboot 获取临时目录

@PostMapping("upload")
@ResponseBody
//将上传的文件放在tomcat目录下面的file文件夹中
public String upload(MultipartFile multipartFile, HttpServletRequest request, HttpServletResponse response) throws IOException {
//获取到原文件全名
String originalFilename = multipartFile.getOriginalFilename();
// request.getServletContext()。getRealPath("")这里不能使用这个,这个是获取servlet的对象,并获取到的一个临时文件的路径,所以这里不能使用这个
//这里获取到我们项目的根目录,classpath下面
String realPath = ResourceUtils.getURL(ResourceUtils.CLASSPATH_URL_PREFIX).getPath();
SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd");
String format = simpleDateFormat.format(new Date());
//文件夹路径,这里以时间作为目录
String path = realPath + "static/" + format;
//判断文件夹是否存在,存在就不需要重新创建,不存在就创建
File file = new File(path);
if (!file.exists()) {
file.mkdirs();
}

//转换成对应的文件存储,new File第一个参数是目录的路径,第二个参数是文件的完整名字
multipartFile.transferTo(new File(file, originalFilename));

//上传文件的全路径
String url = request.getScheme() + "://" + request.getServerName() + ":" + request.getServerPort() + "/" + format + "/" + originalFilename;
return url;

}

2、多文件上传
多文件上传需要有multiple属性,我们也可以使用accept来利用前端来进行上传文件的指定

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>上传文件</title>
<script src="https://cdn.bootcss.com/jquery/3.4.1/jquery.min.js"></script>
</head>
<body>


<form action="/upload1" method="post" enctype="multipart/form-data">
<input type="file" name="files" multiple value="多文件上传"> <!--accept可以指定上传文件类型,例如 accept="image/*"-->
<input type="submit" value="多文件上传">
</form>

</script>
</body>
</html>

多文件的上传跟单文件其实差不多,就是对多文件遍历

@PostMapping("upload1")
@ResponseBody
//将上传的文件放在tomcat目录下面的file文件夹中
public String upload1(MultipartFile[] files, HttpServletRequest request, HttpServletResponse response) throws IOException {
List<String> list = new ArrayList<>();
//System.out.println(files.length);
for (MultipartFile multipartFile : files) {
//获取到文件全名
String originalFilename = multipartFile.getOriginalFilename();
// request.getServletContext()。getRealPath("")这里不能使用这个,这个是获取servlet的对象,并获取到的一个临时文件的路径,所以这里不能使用这个
//这里获取到我们项目的根目录,classpath下面
String realPath = ResourceUtils.getURL(ResourceUtils.CLASSPATH_URL_PREFIX).getPath();
SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd");
String format = simpleDateFormat.format(new Date());
//文件夹路径,这里以时间作为目录
String path = realPath + "static/" + format;
//判断文件夹是否存在,存在就不需要重新创建,不存在就创建
File file = new File(path);
if (!file.exists()) {
file.mkdirs();
}

//转换成对应的文件存储,new File第一个参数是目录的路径,第二个参数是文件的完整名字
multipartFile.transferTo(new File(file, originalFilename));

//上传文件的全路径,一般是放在tomcat中
String url = request.getScheme() + "://" + request.getServerName() + ":" + request.getServerPort() + "/" + format + "/" + originalFilename;
//System.out.println("url=》》》》》"+url);
list.add(url);
}
return new ObjectMapper().writeValueAsString(list);
}

3、下载、在线查看
进行文件下载的时候注意我们最好不要创建一个文件大小的数组来直接读取我们的文件,在文件过大的情况下,可能会导致内存溢出,可以根据实际情况,调整一次读取文件的大小,多次进行读取并输出

@RequestMapping("download")
public void download(HttpServletRequest request, HttpServletResponse response, String name, @RequestParam(defaultValue = "0") int isOnline) throws IOException {
try (ServletOutputStream outputStream = response.getOutputStream();) {
/*
这里要使用ResourceUtils来获取到我们项目的根目录
不能使用request.getServletContext().getRealPath("/"),这里获取的是临时文件的根目录(所以不能使用这个)
*/
String path = ResourceUtils.getURL(ResourceUtils.CLASSPATH_URL_PREFIX).getPath();
SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd");
String format = simpleDateFormat.format(new Date());
String realPath = path + "static/" + format + "/" + name;
File file = new File(realPath);
//如果下载的文件不存在
if (!file.exists()) {
response.setContentType("text/html;charset=utf-8");
//response.getWriter().write(str);这种写入的就相当于生成了jsp/html,返回html/jsp,所以需要我们进行contentType的设置
response.getWriter().write("下载的文件不存在");
return;
}

InputStream in = new FileInputStream(realPath);
int read;

//byte[] b = new byte[in.available()];创建一个输入流大小的字节数组,然后把输入流中所有的数据都写入到数组中
byte[] b = new byte[1024];
/*
1、Content-Disposition的作用:告知浏览器以何种方式显示响应返回的文件,用浏览器打开还是以附件的形式下载到本地保存
2、attachment表示以附件方式下载 inline表示在线打开 "Content-Disposition: inline; filename=文件名.mp3"
3、filename表示文件的默认名称,因为网络传输只支持URL编码的相关支付,因此需要将文件名URL编码后进行传输,前端收到后需要反编码才能获取到真正的名称
*/
/* 注意:文件名字如果是中文会出现乱码:解决办法:
1、将name 替换为 new String(filename.getBytes(), "ISO8859-1");
2、将name 替换为 URLEncoder.encode(filename, "utf-8");
*/
if (isOnline == 0) {
//在线打开
response.addHeader("Content-Disposition", "inline;filename=" + URLEncoder.encode(name, "utf-8"));
} else {
//下载形式,一般跟application/octet-stream一起使用
response.addHeader("Content-Disposition", "attachment;filename=" + URLEncoder.encode(name, "utf-8"));
//如果单纯写这个也可以进行下载功能,表示以二进制流的形式
response.setContentType("application/octet-stream");
}
//文件大小
response.addHeader("Content-Length", "" + file.length());
while ((read = in.read(b)) > 0) {
outputStream.write(b);
}
} catch (Exception e) {
System.out.println(e.getMessage());
throw e;
}
}

4、上传文件大小限制

 

默认的单文件大小是1m,多文件的总大小10m,超过就会报MaxUploadSizeExceededException异常

我们根据实际情况进行调整

例如

 

 

5、自定义上传文件超过限制异常

springboot自带的异常显示很多时候都不是我们自己想要的,那么对于这种异常,我们可以使用自定义异常来进行显示

package org.springframework.myspringboot.exception;

import org.apache.tomcat.util.http.fileupload.impl.FileSizeLimitExceededException;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.multipart.MaxUploadSizeExceededException;
import org.springframework.web.multipart.MultipartFile;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.validation.constraints.Max;
import java.io.IOException;

/**
* @author winnie
* @PackageName:org.springframework.myspringboot.exception
* @Description TODO
* @date 2022/8/3 18:25
*/
@ControllerAdvice
public class MyCustomMaxUploadSizeException {
//这里注意如果是参数中有不存在的参数就会导致我们的全局异常失效
@ExceptionHandler(value = MaxUploadSizeExceededException.class)
@ResponseBody
public String MyException(HttpServletRequest request,
HttpServletResponse response,
MaxUploadSizeExceededException e
)throws IOException {

// response.setContentType("text/html;charset=utf-8");
// response.getWriter().write("上传的文件的大小是超过了最大限制");
return "上传的文件的大小是超过了最大限制";
}
}

这里是一个比较简单的demo,我们也可以返回自定义的错误视图

例如,引入thymeleaf依赖

<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
<version>2.2.5.snapshot</version>
</dependency>
package org.springframework.myspringboot.exception;


import org.springframework.boot.logging.java.SimpleFormatter;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.multipart.MaxUploadSizeExceededException;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.View;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.Date;

/**
* @author winnie
* @PackageName:org.springframework.myspringboot.exception
* @Description TODO
* @date 2022/8/3 18:25
*/
@ControllerAdvice
public class MyCustomMaxUploadSizeException {
//这里注意如果是参数中有不存在的参数就会导致我们的全局异常失效
@ExceptionHandler(value = MaxUploadSizeExceededException.class)
public ModelAndView MyException(HttpServletRequest request,
HttpServletResponse response,
MaxUploadSizeExceededException e
)throws IOException {

// response.setContentType("text/html;charset=utf-8");
// response.getWriter().write("上传的文件的大小是超过了最大限制");
ModelAndView modelAndView = new ModelAndView();
modelAndView.addObject("error",e.getMessage());
modelAndView.addObject("date",new SimpleDateFormat("yyyy-MM-dd").format(new Date()));
modelAndView.setViewName("uploadException.html");
return modelAndView;
}
}

<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>上传文件大小超过限制</title>
</head>
<body>
上传文件超过大小<br><br>
发生异常的日期:<p th:text="${date}"></p>
异常信息:<p th:text="${error}"></p>
</body>
</html>
发生异常的时候

 

参考文章:http://blog.ncmem.com/wordpress/2023/12/04/springboot%e6%96%87%e4%bb%b6%e4%b8%8a%e4%bc%a0%e4%b8%8b%e8%bd%bd/

欢迎入群一起讨论