Spring官方例子中文翻译--用thymeleaf模板上传文件

本次讲的内容是,使用thymeleaf模板上传文件,spring在后台做处理。springboot默认的模板引擎是thymeleaf,需要掌握一下。
** 程序结构**

└── src
    └── main
        └── java
            └── hello

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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>org.springframework</groupId>
    <artifactId>gs-uploading-files</artifactId>
    <version>0.1.0</version>

    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.1.6.RELEASE</version>
    </parent>

    <properties>
        <java.version>1.8</java.version>
    </properties>


    <dependencies>
        <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>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>

</project>

Spring Boot将会你做如下的事:

  • 将 classpath 里面所有用到的jar包构建成一个可执行的 JAR 文件,方便执行你的程序
  • 搜索public static void main()方法并且将它当作可执行类
  • 根据springboot版本,去查找相应的依赖类版本,当然你可以定义其它版本。
    创建一个Application类

为了启动一个Spring Boot MVC应用程序,我们首先需要一个启动器,这里spring-boot-starter-thymeleafspring-boot-starter-web已经添加作为依赖项。要在servlet容器中上载文件,
需要注册MultipartConfigElement类(在web.xml中为<multipart-config>)。由于Spring Boot,一切都是自动配置!

开始使用此应用程序所需的只是以下应用程序类。
src/main/java/hello/Application.java

package hello;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class Application {

    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }

}

作为自动配置SpringMVC的一部分,SpringBoot将创建一个MultipartConfigElement bean,并为文件上载做好准备。

创建文件上传控制器
最初的应用程序已经包含一些类来处理在磁盘上存储和加载上传的文件;它们都位于hello.storage包中。我们将在新的FileUploadController中使用它们。

src/main/java/hello/FileUploadController.java

package hello;

import java.io.IOException;
import java.util.stream.Collectors;

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.ExceptionHandler;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.servlet.mvc.method.annotation.MvcUriComponentsBuilder;
import org.springframework.web.servlet.mvc.support.RedirectAttributes;

import hello.storage.StorageFileNotFoundException;
import hello.storage.StorageService;

@Controller
public class FileUploadController {

    private final StorageService storageService;

    @Autowired
    public FileUploadController(StorageService storageService) {
        this.storageService = storageService;
    }

    @GetMapping("/")
    public String listUploadedFiles(Model model) throws IOException {

        model.addAttribute("files", storageService.loadAll().map(
                path -> MvcUriComponentsBuilder.fromMethodName(FileUploadController.class,
                        "serveFile", path.getFileName().toString()).build().toString())
                .collect(Collectors.toList()));

        return "uploadForm";
    }

    @GetMapping("/files/{filename:.+}")
    @ResponseBody
    public ResponseEntity<Resource> serveFile(@PathVariable String filename) {

        Resource file = storageService.loadAsResource(filename);
        return ResponseEntity.ok().header(HttpHeaders.CONTENT_DISPOSITION,
                "attachment; filename=\"" + file.getFilename() + "\"").body(file);
    }

    @PostMapping("/")
    public String handleFileUpload(@RequestParam("file") MultipartFile file,
            RedirectAttributes redirectAttributes) {

        storageService.store(file);
        redirectAttributes.addFlashAttribute("message",
                "You successfully uploaded " + file.getOriginalFilename() + "!");

        return "redirect:/";
    }

    @ExceptionHandler(StorageFileNotFoundException.class)
    public ResponseEntity<?> handleStorageFileNotFound(StorageFileNotFoundException exc) {
        return ResponseEntity.notFound().build();
    }

}

这个类是用@Controller 注释的,所以SpringMVC可以提取它并查找路由。每个方法都用@GetMapping 或@PostMapping标记,将路径和HTTP操作绑定到特定的控制器。
在这个例子中:
GET / 查找从StorageService上传的文件的当前列表,并将其加载到一个Thymeleaf模板中。它使用MvcUriComponentsBuilder映射到实际资源的链接
GET /files/{filename} 加载资源(如果存在),并使用"Content-Disposition"响应头将其发送到浏览器以便进行下载
POST / 用于处理多媒体文件,并将其交给StorageService进行保存。

在生产场景中,您更可能将文件存储在临时位置、数据库或NoSQL存储区(如Mongo的GridFS)。最好不要文件和文字内容放在一个服务器上。
您需要为控制器提供一个StorageService接口,以便与存储层(如文件系统)交互。接口如下:

src/main/java/hello/storage/StorageService.java

package hello.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 {

    void init();

    void store(MultipartFile file);

    Stream<Path> loadAll();

    Path load(String filename);

    Resource loadAsResource(String filename);

    void deleteAll();

}

示例应用程序中有接口的示例实现。如果你想节省时间,你可以复制粘贴它。

创建一个简单的HTML模板
src/main/resources/templates/uploadForm.html

<html xmlns:th="https://www.thymeleaf.org">
<body>

    <div th:if="${message}">
        <h2 th:text="${message}"/>
    </div>

    <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>
    </div>

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

</body>
</html>

模板由三部分组成:

在顶部的可选消息,SpringMVC在其中编写一个flash范围的消息。

  • 允许用户上载文件的窗体
  • 从后端提供的文件列表
  • 文件上载限制

通过Spring引导,我们可以使用一些属性设置来调整其自动配置的MultipartConfigElement。
把下面加到你的配置文件:
src/main/resources/application.properties

spring.servlet.multipart.max-file-size=128KB
spring.servlet.multipart.max-request-size=128KB

spring.http.multipart.max-file-size是设置为128KB,总文件大小不能超过128KB的意思。
spring.http.multipart.max-request-size 128KB的意思是设置为一个请求的大小,multipart/form-data能超过128KB。

使程序运行
希望将文件上载到一个目标文件夹,因此,让我们增强基本应用程序类,并添加一个 Boot CommandLineRunner运行程序,它在启动时删除并重新创建该文件夹:

src/main/java/hello/Application.java

package hello;

import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;

import hello.storage.StorageProperties;
import hello.storage.StorageService;

@SpringBootApplication
@EnableConfigurationProperties(StorageProperties.class)
public class Application {

    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }

    @Bean
    CommandLineRunner init(StorageService storageService) {
        return (args) -> {
            storageService.deleteAll();
            storageService.init();
        };
    }
}

@SpringBootApplication包含如下注解:

  • @Configuration 将类标记为应用程序上下文的bean定义源。
  • @EnableAutoConfiguration 告诉SpringBoot根据类路径设置、其他bean和各种属性设置开始添加bean。
  • @ComponentScan 告诉Spring在hello包中查找其他组件、配置和服务。
    您注意到没有一行XML吗?也没有web.xml文件。这个Web应用程序是100%纯Java,您不必麻烦的基础配置

** 运行你的程序 **
STS下(Maven可参考前面文章):右键-选择Run as-Spring Boot App

服务器端部分接收上传的文件,并显示日志输出。
当服务器运行时,您需要打开浏览器并访问 http://localhost:8080/
查看上载表单。选择一个(小)文件并按“Upload”,您应该可以从控制器看到成功页面。选择一个太大的文件,你会得到一个的错误页面。
然后,您应该在浏览器窗口中看到类似的内容:

您成功上传了xxxx!

测试你的应用程序
在我们的应用程序中有多种方法可以测试这个功能性。下面是一个利用mockmvc的示例,因此不需要启动servlet容器:
src/test/java/hello/FileUploadTests.java

package hello;

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

import org.hamcrest.Matchers;
import org.junit.Test;
import org.junit.runner.RunWith;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.mock.mockito.MockBean;
import org.springframework.mock.web.MockMultipartFile;
import org.springframework.test.context.junit4.SpringRunner;
import org.springframework.test.web.servlet.MockMvc;

import static org.mockito.BDDMockito.given;
import static org.mockito.BDDMockito.then;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.fileUpload;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.header;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.model;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;

import hello.storage.StorageFileNotFoundException;
import hello.storage.StorageService;

@RunWith(SpringRunner.class)
@AutoConfigureMockMvc
@SpringBootTest
public class FileUploadTests {

    @Autowired
    private MockMvc mvc;

    @MockBean
    private StorageService storageService;

    @Test
    public void shouldListAllFiles() throws Exception {
        given(this.storageService.loadAll())
                .willReturn(Stream.of(Paths.get("first.txt"), Paths.get("second.txt")));

        this.mvc.perform(get("/")).andExpect(status().isOk())
                .andExpect(model().attribute("files",
                        Matchers.contains("http://localhost/files/first.txt",
                                "http://localhost/files/second.txt")));
    }

    @Test
    public void shouldSaveUploadedFile() throws Exception {
        MockMultipartFile multipartFile = new MockMultipartFile("file", "test.txt",
                "text/plain", "Spring Framework".getBytes());
        this.mvc.perform(fileUpload("/").file(multipartFile))
                .andExpect(status().isFound())
                .andExpect(header().string("Location", "/"));

        then(this.storageService).should().store(multipartFile);
    }

    @SuppressWarnings("unchecked")
    @Test
    public void should404WhenMissingFile() throws Exception {
        given(this.storageService.loadAsResource("test.txt"))
                .willThrow(StorageFileNotFoundException.class);

        this.mvc.perform(get("/files/test.txt")).andExpect(status().isNotFound());
    }

}

在这些测试中,我们使用各种模拟来设置与控制器和StorageService的交互,也使用FileUploadIntegrationTests设置与servlet容器本身的交互。

有关集成测试的示例,请查看FileUploadIntegrationTests类。

我利用业余时间,翻译了Spring官网的例子,方便中文不好的同学,将陆续发到头条上,欢迎大家关注,也可以上我个人BLOG:itmanclub.com,上面有已经翻译过的。
在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值