spring官网演示实例(文件上传下载)

本文介绍了如何使用Spring Boot构建一个上传文件的项目,涉及Path和Files类方法的应用,接口设计,以及配置和异常处理。适合初级开发者学习文件操作和接口拆分原则。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

根据该项目练习可以有效加强Path,Files类方法使用,并通过接口设计理解一个功能的接口拆分思想,和一个项目基本结构的设计思想等等,对初级开发来讲都是很有意义的。自己相对于官网的示例做了一些局部的优化,避免了一些使得示例演示看起来不太舒服小问题。

在这里插入图片描述

1.项目依赖

<?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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <groupId>com.example</groupId>
    <artifactId>uploadfiles</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>uploadfiles</name>
    <description>Demo project for Spring Boot</description>

    <properties>
        <java.version>1.8</java.version>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
        <spring-boot.version>2.3.7.RELEASE</spring-boot.version>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-thymeleaf</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
            <exclusions>
                <exclusion>
                    <groupId>org.junit.vintage</groupId>
                    <artifactId>junit-vintage-engine</artifactId>
                </exclusion>
            </exclusions>
        </dependency>
    </dependencies>

    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-dependencies</artifactId>
                <version>${spring-boot.version}</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>

    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>3.8.1</version>
                <configuration>
                    <source>1.8</source>
                    <target>1.8</target>
                    <encoding>UTF-8</encoding>
                </configuration>
            </plugin>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
                <version>2.3.7.RELEASE</version>
                <configuration>
                    <mainClass>com.example.uploadfiles.UploadfilesApplication</mainClass>
                </configuration>
                <executions>
                    <execution>
                        <id>repackage</id>
                        <goals>
                            <goal>repackage</goal>
                        </goals>
                    </execution>
                </executions>
            </plugin>
        </plugins>
    </build>

</project>

2.目录结构
在这里插入图片描述
3.文件配置

# 应用名称
spring.application.name=uploadfiles

# 应用服务 WEB 访问端口
server.port=8080

# THYMELEAF (ThymeleafAutoConfiguration)
# 开启模板缓存(默认值: true )
spring.thymeleaf.cache=true
# 检查模板是否存在,然后再呈现
spring.thymeleaf.check-template=true
# 检查模板位置是否正确(默认值 :true )
spring.thymeleaf.check-template-location=true
#Content-Type 的值(默认值: text/html )
spring.thymeleaf.content-type=text/html
# 开启 MVC Thymeleaf 视图解析(默认值: true )
spring.thymeleaf.enabled=true
# 模板编码
spring.thymeleaf.encoding=UTF-8
# 要被排除在解析之外的视图名称列表,⽤逗号分隔
spring.thymeleaf.excluded-view-names=
# 要运⽤于模板之上的模板模式。另⻅ StandardTemplate-ModeHandlers( 默认值: HTML5)
spring.thymeleaf.mode=HTML5
# 在构建 URL 时添加到视图名称前的前缀(默认值: classpath:/templates/ )
spring.thymeleaf.prefix=classpath:/templates/
# 在构建 URL 时添加到视图名称后的后缀(默认值: .html )
spring.thymeleaf.suffix=.html
storage.a.location=root
spring.servlet.multipart.max-file-size=100MB

4.核心接口

package com.example.uploadfiles.storage;

import org.springframework.core.io.Resource;
import org.springframework.web.multipart.MultipartFile;

import java.nio.file.Path;
import java.util.stream.Stream;

public interface StorageService {

    /**
     * @Author xiefenghong
     * @Description 初始化文件目录
     * @Date 13:53 2022/2/24
     * @Param []
     * @return void
     **/
    Path init();

    /**
     * @Author xiefenghong
     * @Description 上传文件
     * @Date 13:53 2022/2/24
     * @Param [file]
     * @return void
     **/
    void store(MultipartFile file);

    /**
     * @Author xiefenghong
     * @Description 获取文件目录下所有文件路径
     * @Date 13:53 2022/2/24
     * @Param []
     * @return java.util.stream.Stream<java.nio.file.Path>
     **/
    Stream<Path> loadAll();

    /*
     * @Author xiefenghong
     * @Description 获取文件目录下指定文件路径
     * @Date 13:54 2022/2/24
     * @Param [filename]
     * @return java.nio.file.Path
     **/
    Path load(String filename);

    /**
     * @Author xiefenghong
     * @Description 获取文件目录下指定文件
     * @Date 13:55 2022/2/24
     * @Param [filename]
     * @return org.springframework.core.io.Resource
     **/
    Resource loadAsResource(String filename);

    /**
     * @Author xiefenghong
     * @Description 删除文件目录下所有文件
     * @Date 13:55 2022/2/24
     * @Param []
     * @return void
     **/
    void deleteAll();
}

5.配置文件初始化属性——目录路径

package com.example.uploadfiles.storage;

import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;

/**
 * @author xiefenghong
 * @version V1.0
 * @date 2022/2/22
 * @description
 */
@ConfigurationProperties(prefix="storage.a",ignoreInvalidFields = true)
@Data
@Component
public class StorageProperties {

    private String location ;
}

6.自定义异常类

package com.example.uploadfiles.storage;

/**
 * @author xiefenghong
 * @version V1.0
 * @date 2022/2/22
 * @description
 */
public class StorageException extends RuntimeException {
    public StorageException(String message){
        super(message);
    }
    public StorageException(String message,Throwable throwable){
        super(message,throwable);
    }
}
package com.example.uploadfiles.storage;

/**
 * @author xiefenghong
 * @version V1.0
 * @date 2022/2/22
 * @description
 */
public class StorageFileNotFoundException extends StorageException {
    public StorageFileNotFoundException(String message) {
        super(message);
    }

    public StorageFileNotFoundException(String message, Throwable throwable) {
        super(message, throwable);
    }
}

7.接口实现类

package com.example.uploadfiles.storage;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.io.Resource;
import org.springframework.core.io.UrlResource;
import org.springframework.stereotype.Service;
import org.springframework.util.FileSystemUtils;
import org.springframework.web.multipart.MultipartFile;

import javax.annotation.PostConstruct;
import java.io.IOException;
import java.net.MalformedURLException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.stream.Stream;

/**
 * @author xiefenghong
 * @version V1.0
 * @date 2022/2/22
 * @description
 */
@Service
public class FileSyestemStorageService implements StorageService {
    private Path path;
    @Autowired
    private StorageProperties storageProperties;
    @PostConstruct
    public void start(){
        this.path =Paths.get(storageProperties.getLocation());
    }

    @Override
    public Path init() {
        if (Files.notExists(path)){
            try {
                return Files.createDirectory(path);
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        return path;
    }

    @Override
    public void store(MultipartFile file) {
        if (file.isEmpty()){
            throw new StorageFileNotFoundException("下载的文件为空!");
        }
        if (!Files.exists(path)){
            throw new StorageFileNotFoundException("请先初始化目录!");
        }
        try {
            Files.copy(file.getInputStream(),load(file.getOriginalFilename()));
        } catch (IOException e) {
            throw new StorageException("文件上传失败!"+e);
        }
    }

    @Override
    public Stream<Path> loadAll() {
        if (!Files.exists(path)){
            return Stream.empty();
        }
        try {
            return Files.walk(this.path,1).filter(path ->!path.equals(this.path))
                    .map(path -> this.path.relativize(path));
        } catch (IOException e) {
            throw new StorageException("下载所有文件失败"+e);
        }
    }

    @Override
    public Path load(String filename) {
        return path.resolve(filename);
    }

    @Override
    public Resource loadAsResource(String filename) {
        Path path = load(filename);
        try {
            UrlResource resource = new UrlResource(path.toUri());
            if (!(resource.exists()||resource.isReadable())) {
                throw new StorageFileNotFoundException("无法下载该文件:"+filename);
            }
            return resource;
        } catch (MalformedURLException e) {
            throw new StorageFileNotFoundException("无法下载该文件:"+filename);
        }
    }

    @Override
    public void deleteAll() {
        FileSystemUtils.deleteRecursively(path.toFile());
        try {
            FileSystemUtils.deleteRecursively(path);
        } catch (IOException e) {
            throw new StorageFileNotFoundException("删除目录失败!");
        }
    }
}

8.控制层接口

package com.example.uploadfiles;

import com.example.uploadfiles.storage.FileSyestemStorageService;
import com.example.uploadfiles.storage.StorageFileNotFoundException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.io.Resource;
import org.springframework.http.HttpHeaders;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.servlet.mvc.method.annotation.MvcUriComponentsBuilder;
import org.springframework.web.servlet.mvc.support.RedirectAttributes;
import org.springframework.web.util.UriComponentsBuilder;

import java.nio.file.Path;
import java.util.stream.Collectors;

/**
 * @author xiefenghong
 * @version V1.0
 * @date 2022/2/22
 * @description
 */
@Controller
public class FileUploadController {

    @Autowired
    private FileSyestemStorageService fileService;
    /*
     * @Author xiefenghong
     * @Description 初始化文档根目录
     * @Date 15:37 2022/2/23
     * @Param []
     * @return void
     **/
    @RequestMapping("init")
    public String initDirectory(RedirectAttributes redirectAttributes){
        Path path = fileService.init();
        redirectAttributes.addFlashAttribute("message","你已成功初始化目录"+path);
        return "redirect:/";
    }
    @GetMapping("/")
    public String loadFileHTML(Model model){
        model.addAttribute("files",fileService.loadAll().map(path -> {
            UriComponentsBuilder serveFile = MvcUriComponentsBuilder.fromMethodName(FileUploadController.class, "serveFile", path.getFileName().toString());
            return serveFile.build().toUri().toString();
        }).collect(Collectors.toList()));
        return "uploadForm";
    }
    // 正则表达式匹配, 语法: {varName:regex} 前面是变量名,后面式表达式
    // 匹配出现过一次或多次.的字符串 如: "xyz.png"
    @GetMapping("files/{filename:.+}")
    public ResponseEntity serveFile(@PathVariable String filename){
        Resource resource = fileService.loadAsResource(filename);
        return ResponseEntity.ok().header(HttpHeaders.CONTENT_DISPOSITION, "attachment;filename=" + filename).body(resource);
    }
    @PostMapping("/")
    public String storeFile(MultipartFile file, RedirectAttributes redirectAttributes){
        fileService.store(file);
        redirectAttributes.addFlashAttribute("message","你已成功上传文件"+file.getOriginalFilename());
        return "redirect:/";
    }
    @RequestMapping("deleteFileAll")
    public String deleteFile(RedirectAttributes redirectAttributes){
        fileService.deleteAll();
        redirectAttributes.addFlashAttribute("message","你已成功删除所有文件");
        return "redirect:/";
    }

    @ExceptionHandler(StorageFileNotFoundException.class)
    public String fileNotFoundExceptionHandler(StorageFileNotFoundException exception, RedirectAttributes redirectAttributes){
        redirectAttributes.addFlashAttribute("message","操作异常:"+exception.getMessage());
        return "redirect:/";
    }
}

9.html页面

<html xmlns:th="https://www.thymeleaf.org">
<body>
<div th:if="${message}">
    <h2 th:text="${message}"/>
</div>
<a href="/init">初始化目录</a>
<div>
    <form method="POST" enctype="multipart/form-data" action="/">
        <table>
            <tr><td>File to upload:</td><td><input type="file" name="file" /></td></tr>
            <tr><td></td><td><input type="submit" value="Upload" /></td></tr>
        </table>
    </form>
    <form method="POST" action="/deleteFileAll">
        <table>
            <tr><td></td><td><input type="submit" value="deleteAll" /></td></tr>
        </table>
    </form>
</div>

<div>
    <ul>
        <li th:each="file : ${files}">
            <a th:href="${file}" th:text="${file}" />
        </li>
    </ul>
</div>

</body>
</html>
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值