这是一个文件上传 和 文件下载的工具类,用了很多的api和类的使用,听完之后感觉很难写,很多知识点都不太清楚,一直不想写,写的时候上传的思路理清楚了,但是有很多方法使用查了半天。上传使用了时间为多级目录,拼接路径、文件名、后缀实现文件上传,multipartFile.transferTo()去保存文件,之后直接访问。下载用的是流处理文件,ResponseEntity实现文件下载功能。
1.文件上传
用户将他本地的一个文件上传到后台服务器,后台服务器会将这个文件持久化保存起来,然后会将这个文件的访问路径告诉用户
1.1 实现步骤
1.1.1 用户将他本地 的文件进行上传:表单提交文件
1.1.2 后台服务器接收到用户上传的文件:以对象的方式
1.1.3 后台服务器将接收到的文件持久化保存
1.1.4 将文件的访问路径返回给用户
1.2 代码实现
用SpringMvc实现,引入依赖,写配置文件
1.2.1 pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.atguigu</groupId>
<artifactId>day06-spring-file</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>war</packaging>
<properties>
<maven.compiler.source>17</maven.compiler.source>
<maven.compiler.target>17</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<dependencies>
<!--1. springmvc依赖-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>6.0.6</version>
</dependency>
<!--2. 文件上传的依赖-->
<dependency>
<groupId>commons-fileupload</groupId>
<artifactId>commons-fileupload</artifactId>
<version>1.3.1</version>
</dependency>
<!--servlet相关的依赖-->
<dependency>
<groupId>jakarta.platform</groupId>
<artifactId>jakarta.jakartaee-web-api</artifactId>
<version>9.1.0</version>
<scope>provided</scope>
</dependency>
</dependencies>
</project>
1.2.2 web.xml
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee
http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
version="4.0">
<servlet>
<servlet-name>dispatcherServlet</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:spring-web.xml</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
<!--文件上传下载时,文件的的要求配置-->
<multipart-config>
<!-- 定义文件上传时所需的最大值,单位为字节 -->
<max-file-size>10485760</max-file-size>
<!-- 定义单个上传文件的最大值,单位为字节 -->
<max-request-size>20971520</max-request-size>
<!-- 定义内存中存储文件的最大值,超过此大小的文件会写入到硬盘中 -->
<file-size-threshold>5242880</file-size-threshold>
</multipart-config>
</servlet>
<servlet-mapping>
<servlet-name>dispatcherServlet</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
</web-app>
1.2.3 spring-web.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:mvc="http://www.springframework.org/schema/mvc"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/mvc https://www.springframework.org/schema/mvc/spring-mvc.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd">
<!--包扫描-->
<context:component-scan base-package="com.atguigu"/>
<!--mvc启动配置-->
<mvc:annotation-driven/>
<!--配置文件解析器-->
<bean id="multipartResolver" class="org.springframework.web.multipart.support.StandardServletMultipartResolver">
</bean>
<!--静态资源处理器-->
<mvc:default-servlet-handler></mvc:default-servlet-handler>
</beans>
1.2.4 index.html
编写上传页面, 但是表单的method属性为post,表单的enctype属性multipart/form-data
input的type为file,这个input一定要有name属性
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>文件传输首页</title>
</head>
<body>
<!--
只是写一个表单按钮
method="post" 因为我们要传输文件 而get只能识别String
enctype="multipart/form-data"
-->
<form action="/file/upload" method="post" enctype="multipart/form-data">
<input type="file" name="file"> <br/>
<button >提交</button>
</form>
</body>
</html>
1.2.5 编写控制器方法 uploadFile
前面在配置文件中已经引入文件上传的依赖,在spring-web.xml中配置文件解析器
但是注意的是: 1. 在处理文件上传请求的方法中以MultipartFile类型接收上传的文件 2. 保存的时候文件名一定要唯一,所以后端保存文件的时候需要修改文件名,但是需要保证决不能改后缀 3. 保证一个目录中存储的内容数量不能过多,一般情况下保证最少有两级子目
文件的存储位置:
(1) 当前项目部署的服务器中
(2) 公司自己搭建的服务器中:FastDFS、MinIO等等
(3) 云服务器中:阿里云、腾讯云、华为云、亚马逊云等等
@RestController
@RequestMapping("/file")
public class FileController {
/**
* ServletContext.getRealPath()的使用
* ServletContext.getRealPath() 是从当前servlet 在tomcat 中的存放文件夹开始计算起的
* 在使用ServletContext.getRealPath() 时,传入的参数是从 当前servlet 部署在tomcat中的文件夹算起的相对路径,
* 要以"/" 开头,否则会找不到路径,导致NullPointerException
*/
@Autowired
private ServletContext servletContext;
/**
* 文件上传
* @param multipartFile
* MultipartFile类是Spring专门用来通过表单来上传文件的
* getOriginalFileName方法获取的是文件的完整名称,包括文件名称+文件拓展名。
* transferTo方法用来将接收文件传输到给定目标路径,会抛出IOException、IllegalStateException异常。
*/
@PostMapping("upload")
public String uploadFile( @RequestParam ("file") MultipartFile multipartFile) throws IOException {
//将文件上传到服务器 file文件中
//1.1获取文件名 + 后缀
String filename = multipartFile.getOriginalFilename();
System.out.println("filename = " + filename);
//1.2获取文件后缀
int index = filename.indexOf(".");
System.out.println("index = " + index);
String subs = filename.substring(index);
System.out.println("subs = " + subs);
//1.3每个文件都是唯一是所以要给文件名名,生成文件名
String uuid = UUID.randomUUID().toString().replaceAll("-","")+subs;
System.out.println("uuid = " + uuid);
//2.保存 一个存储目录中不能有太多的文件
//得到当前文件部署路径 这到底有没有/
String realPath = servletContext.getRealPath("/");
//2.2跟据年月日创建文件目录
String dateFilePath = FileUtils.buildDateFilePath("file");
//拼接路径
String fileDirPath = realPath + dateFilePath;
System.out.println("fileDirPath = " + fileDirPath);
//2.3判断创建的文件是否存在,
//new File();创建文件,但是这里的file并没有被创建,而只是创建了一个File对象。
//创建之后需要检查文件路径是否有创建成功,如果没有创建成功,则需要使用mkdir()或者mkdirs()重新创建路径。
File file = new File(fileDirPath);
//检查文件路径是否有创建成功
if(!file.exists()){
file.mkdirs();
}
// 然后用 MultipartFile 保存文件
multipartFile.transferTo(new File(file,uuid));
//返文件路径
return "http://localhost:8080/" + dateFilePath + uuid;
}
1.2.5 工具类
public class FileUtils {
private static String SEPARATOR = "/";
private static String FORMAT_PATTERN = "yyyy/MM/dd";
/**
* 构架文件上传的路径 按照时间
* @param dirPath
* @return
*/
public static String buildDateFilePath(String dirPath){
//创建StringBuilder拼接文件
dirPath = dirPath == null ? "" : dirPath;
//使用StringBuilder的原因就是为了更好的操作字符串
StringBuilder stringBuilder = new StringBuilder(dirPath);
//处理dirPath后缀 就是有文件的时候在文件路径后加 /
dirPathExtension(dirPath,stringBuilder);
//获取当前日期
String dataPath = new SimpleDateFormat(FORMAT_PATTERN).format(new Date());
//返回的string就是文件按照时间创建的路径后加/转string
return stringBuilder.append(dataPath).append(SEPARATOR).toString();
}
/**
* 处理dirPath的后缀
* @param dirPath
* @param stringBuilder
*/
private static void dirPathExtension(String dirPath, StringBuilder stringBuilder) {
if(dirPath != null){
if(!dirPath.endsWith(SEPARATOR)){
stringBuilder.append(SEPARATOR);
}
}
}
/**
* 将原文件名转成一个唯一的文件名
* @param originalFilename
* @return
*/
public static String getUUIDName(String originalFilename){
return UUID.randomUUID().toString().replaceAll("-","") + originalFilename.substring(originalFilename.lastIndexOf("."));
}
}
1.2.6 运行结果为
返回的路径就直接能在浏览器打开。(端口要一致)
2.文件下载
文件下载就是将服务器的资源下载到本地。
2.1 代码实现
2.1.1 index.html
提供下载连接
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>文件传输首页</title>
</head>
<body>
<!--
只是写一个表单按钮
method="post" 因为我们要传输文件 而get只能识别String
enctype="multipart/form-data"
-->
<form action="/file/upload" method="post" enctype="multipart/form-data">
<input type="file" name="file"> <br/>
<button >提交</button>
</form>
<a href="/file/download?fileName=a.jpg">下载a.jpg</a><br/>
<a href="/file/download?fileName=b.jpg">下载b.jpg</a><br/>
<a href="/file/download?fileName=c.jpg">下载c.jpg</a><br/>
<a href="/file/download?fileName=d.jpg">下载d.jpg</a><br/>
<a href="/file/download?fileName=teacher.txt">下载teacher.txt</a><br/>
</body>
</html>
2.1.2 编写控制器方法 downloadFile
@GetMapping("/download")
public ResponseEntity<byte[]> downloadFile(String fileName) throws IOException {
//其实就是封装响应对象的行头体
//获取服务器中文的真实路径
String realPath = servletContext.getRealPath("/download/" + fileName);
System.out.println("realPath = " + realPath);
//创建输入流
FileInputStream inputStream = new FileInputStream(realPath);
//创建字节数组
byte[] bytes = new byte[inputStream.available()];
//将流读到到字节数组中
inputStream.read(bytes);
//创建HttpHeaders对象设置响应头信息
HttpHeaders httpHeaders = new HttpHeaders();
/**
* 获取MIME类型:
* MIME类型:在互联网通信过程中定义的一种文件数据类型
* 格式: 大类型/小类型 text/html image/jpeg
* 获取:String getMimeType(String file)
*/
//往响应头添加两个头:
// Content-Type告诉客户端我响应的这个文件是什么类型
httpHeaders.add("Content-Type", servletContext.getMimeType(fileName));
//设置要下载方式以及下载文件的名字
httpHeaders.add("Content-Disposition", "attachment;filename" + fileName);
//响应状态码
HttpStatus statusCode = HttpStatus.OK;
//创建ResponseEntity对象
ResponseEntity<byte[]> responseEntity = new ResponseEntity<byte[]>(bytes, httpHeaders, statusCode);
//关闭流
inputStream.close();
return responseEntity;
}
2.1.3 运行结果为