Spring Boot + thymeleaf 实现文件上传下载功能

网友投稿 597 2023-02-27


Spring Boot + thymeleaf 实现文件上传下载功能

最近同事问我有没有有关于技术的电子书,我打开电脑上的小书库,但是邮件发给他太大了,公司又禁止用文件夹共享,于是花半天时间写了个小的文件上传程序,部署在自己的linux机器上。

提供功能: 1 .文件上传 2.文件列表展示以及下载

原有的上传那块很丑,写了点js代码优化了下,最后界面显示如下图:

先给出成果,下面就一步步演示怎么实现。

1.新建项目

首先当然是新建一个spring-boot工程,你可以选择在网站初始化一个项目或者使用IDE的Spring Initialier功能,都可以新建一个项目。这里我从IDEA新建项目:

下一步,然后输入group和artifact,继续点击next:

这时候出现这个模块选择界面,点击web选项,勾上Web,证明这是一个webapp,再点击Template Engines选择前端的模板引擎,我们选择Thymleaf,spring-boot官方也推荐使用这个模板来替代jsp。

最后一步,然后等待项目初始化成功。

2.pom设置

首先检查项目需要添加哪些依赖,直接贴出我的pom文件:

xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">

4.0.0

com.shuqing28

upload

0.0.1-SNAPSHOT

jar

upload

Demo project for Spring Boot

org.springframework.boot

spring-boot-starter-parent

1.5.9.RELEASE

UTF-8

UTF-8

1.8

org.springframework.boot

spring-boot-starter

org.springframework.boot

spring-boot-starter-thymeleaf

org.springframework.boot

spring-boot-configuration-processor

true

org.springframework.boot

spring-boot-starter-test

test

org.webjars

bootstrap

3.3.5

org.webjars.bower

jquery

2.2.4

org.springframework.boot

spring-boot-maven-plugin

xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">

4.0.0

com.shuqing28

upload

0.0.1-SNAPSHOT

jar

upload

Demo project for Spring Boot

org.springframework.boot

spring-boot-starter-parent

1.5.9.RELEASE

UTF-8

UTF-8

1.8

org.springframework.boot

spring-boot-starter

org.springframework.boot

spring-boot-starter-thymeleaf

org.springframework.boot

spring-boot-configuration-processor

true

org.springframework.boot

spring-boot-starter-test

test

org.webjars

bootstrap

3.3.5

org.webjars.bower

jquery

2.2.4

org.springframework.boot

spring-boot-maven-plugin

可以查看到 spring-boot-starter-thymeleaf 包含了webapp,最后两个webjars整合了bootstrap和jquery,其它的等代码里用到再说。

最后一个Spring boot maven plugin是系统创建时就添加的,它有以下好处:

1 . 它能够打包classpath下的所有jar,构建成一个可执行的“ber-jar”,方便用户转移服务

2 . 自动搜索 public static void main() 方法并且标记为可执行类

3 . 根据spring-boot版本,提供内建的依赖解释。

3. 上传文件控制器

如果你只是使用SpringMVC上传文件,是需要配置一个 MultipartResolver 的bean的,或者在 web.xml 里配置一个 ,不过借助于spring-boot的自动配置,你什么都不必做。直接写控制器类,我们在 src/main/java 下新建controller的package,并且新建FileUploadController:

package com.shuqing28.upload.controller;

import com.shuqing28.uploadfiles.pojo.Linker;

import com.shuqing28.uploadfiles.exceptions.StorageFileNotFoundException;

import com.shuqing28.uploadfiles.service.StorageService;

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.annotathttp://ion.*;

import org.springframework.web.multipart.MultipartFile;

import org.springframework.web.servlet.mvc.method.annotation.MvcUriComponentsBuilder;

import org.springframework.web.servlet.mvc.support.RedirectAttributes;

import java.io.IOException;

import java.util.List;

import java.util.stream.Collectors;

@Controller

public class FileUploadController {

private final StorageService storageService;

@Autowired

public FileUploadController(StorageService storageService) {

this.storageService = storageService;

}

@GetMapping("/")

public String listUploadedFiles(Model model)throws IOException {

List linkers = storageService.loadAll().map(

path -> new Linker(MvcUriComponentsBuilder.fromMethodName(FileUploadController.class,

"serveFile", path.getFileName().toString()).build().toString(),

path.getFileName().toString())

).collect(Collectors.toList());

model.addAttribute("linkers", linkers);

return "uploadForm";

}

@GetMapping("/files/{filename:.+}")

@ResponseBody

public ResponseEntity 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 注解,证明这是一个Controller,每个方法前添加了 @GetMapping 和 @PostMapping 分别相应Get和Post请求。

首先是 @GetMapping("/") ,方法 listUploadedFiles ,顾名思义,显示文件列表,这里我们借助于storageService遍历文件夹下的所有文件,并且用map方法提合成了链接和文件名列表,返回了一个Linker对象的数组,Linker对象是一个简单pojo,只包含下面两部分:

private String fileUrl;

private String fileName;

这个方法包含了对Java8中Stream的使用,如果有不理解的可以看看这篇文章 Java8 特性详解(二) Stream API .

接下来是 @GetMapping("/files/{filename:.+}") ,方法是 serveFile ,该方法提供文件下载功能,还是借助于storageservice,后面会贴出storageservice的代码。最后使用ResponseEntity,把文件作为body返回给请求方。

@PostMapping("/") 的 handleFileUpload 使用Post请求来上传文件,参数 @RequestParam("file") 提取网页请求里的文件对象,还是使用storageService来保存对象,最后使用重定向来刷新网页,并且给出成功上传的message。

4. 文件处理

上面Controller调用的很多方法由StorageService提供,我们定义一个接口,包含以下方法:

package com.shuqing28.uploadfiles.service;

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);http://

Stream loadAll();

Path load(String filename);

Resource loadAsResource(String filename);

void deleteAll();

}

因为我这里只是借助于本地文件系统处理文件的长传下载,所以有了以下实现类:

package com.shuqing28.uploadfiles.service;

import com.shuqing28.uploadfiles.exceptions.StorageException;

import com.shuqing28.uploadfiles.exceptions.StorageFileNotFoundException;

import com.shuqing28.uploadfiles.config.StorageProperties;

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.util.StringUtils;

import org.springframework.web.multipart.MultipartFile;

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.nio.file.StandardCopyOption;

import java.util.stream.Stream;

@Service

public class FileSystemStorageService implements StorageService {

private final Path rootLocation;

@Autowired

public FileSystemStorageService(StorageProperties properties) {

this.rootLocation = Paths.get(properties.getLocation());

}

@Override

public void init() {

try {

Files.createDirectories(rootLocation);

}

catch (IOException e) {

throw new StorageException("Could not initialize storage", e);

}

}

@Override

public void store(MultipartFile file) {

String filename = StringUtils.cleanPath(file.getOriginalFilename());

try {

if (file.isEmpty()) {

throw new StorageException("Failed to store empty file" + filename);

}

if (filename.contains("..")) {

// This is a security check

throw new StorageException(

"Cannot store file with relative path outside current directory "

+ filename);

}

Files.copy(file.getInputStream(), this.rootLocation.resolve(filename), StandardCopyOption.REPLACE_EXISTING);

} catch (IOException e) {

throw new StorageException("Failed to store file" + filename, e);

}

}

@Override

public Stream loadAll() {

try {

return Files.walk(this.rootLocation, 1)

.filter(path -> !path.equals(this.rootLocation))

.map(path->this.rootLocation.relativize(path));

}

catch (IOException e) {

throw new StorageException("Failed to read stored files", e);

}

}

@Override

public Path load(String filename) {

return rootLocation.resolve(filename);

}

@Override

public Resource loadAsResource(String filename) {

try {

Path file = load(filename);

Resource resource = new UrlResource(file.toUri());

if (resource.exists() || resource.isReadable()) {

return resource;

}

else {

throw new StorageFileNotFoundException(

"Could not read file: " + filename);

}

}

catch (MalformedURLException e) {

throw new StorageFileNotFoundException("Could not read file: " + filename, e);

}

}

@Override

public void deleteAll() {

FileSystemUtils.deleteRecursively(rootLocation.toFile());

}

}

这个类也基本运用了Java的NIO,使用Path对象定义了location用于文件的默认保存路径。

先看 store 方法,store接受一个MultipartFile对象作为参数,想比于传统JSP中只是传二进制字节数组,MultipartFile提供了很多方便调用的方法让我们可以获取到上传文件的各项信息:

public interface MultipartFile extends InputStreamSource {

String getName();

String getOriginalFilename();

String getContentType();

boolean isEmpty();

long getSize();

byte[] getBytes() throws IOException;

InputStream getInputStream() throws IOException;

void transferTo(File dest) throws IOException, IllegalStateException;

}

代码里使用了Files的copy方法把文件流拷到location对应的Path里,当然我们也可以使用transferTo方法保存文件, file.transferTo(this.rootLocation.resolve(filename).toFile());

loadAll方法加载该路径下的所有文件Path信息, loadAsResource 则是加载文件为一个Resource对象,再看Controller的代码,最后是接受一个Resource对象作为body返回给请求方。

5. 前端模板

最后定义了前端模板,这里依旧先看代码:

这里重要的地方还是

下面还放了一个list用于展示文件列表,这里我们获取到服务端提供的linkers对象,不断foreach就可以获得里面的两个元素fileUrl和fileName。

这里jquery换成了微软的CDN,webjars的总是引入不进来,不知道什么原因。

其它设置

在 src/main/resources/application.properties 里设置上传文件大小限制

spring.http.multipart.max-file-size=128MB

spring.http.multipart.max-request-size=128MB

另外在``还设置了文件默认保存路径:

package com.shuqing28.uploadfiles.config;

import org.springframework.boot.context.properties.ConfigurationProperties;

@ConfigurationProperties("storage")

public class StorageProperties {

private String location = "/home/jenkins/upload-files/";

public String getLocation() {

return location;

}

public void setLocation(String location) {

this.location = location;

}

}

这里注意,由于StorageProperties的设置,在Application的那个类中要添加上

@EnableConfigurationProperties注解

@SpringBootApplication

@EnableConfigurationProperties(StorageProperties.class)

public class UploadApplication {

public static void main(String[] args) {

SpringApplication.run(UploadApplication.class, args);

}

}

总结

以上所述是给大家介绍的Spring Boot + thymeleaf 实现文件上传下载功能,希望对大家有所帮助,如果大家有任何疑问请给我留言,会及时回复大家的。在此也非常感谢大家对我们网站的支持!


版权声明:本文内容由网络用户投稿,版权归原作者所有,本站不拥有其著作权,亦不承担相应法律责任。如果您发现本站中有涉嫌抄袭或描述失实的内容,请联系我们jiasou666@gmail.com 处理,核实后本网站将在24小时内删除侵权内容。

上一篇:api网关选型
下一篇:接口设计
相关文章

 发表评论

暂时没有评论,来抢沙发吧~