SpringMvc 文件上传文件下载实现

这是一个文件上传 和 文件下载的工具类,用了很多的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 运行结果为

 

 

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值