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

2025-05-27 0 18

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

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

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

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

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

1.新建项目

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

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

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

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

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

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

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

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

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

2.pom设置

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

?

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62
<?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.shuqing28</groupid>

<artifactid>upload</artifactid>

<version>0.0.1-snapshot</version>

<packaging>jar</packaging>

<name>upload</name>

<description>demo project for spring boot</description>

<parent>

<groupid>org.springframework.boot</groupid>

<artifactid>spring-boot-starter-parent</artifactid>

<version>1.5.9.release</version>

<relativepath/> <!-- lookup parent from repository -->

</parent>

<properties>

<project.build.sourceencoding>utf-8</project.build.sourceencoding>

<project.reporting.outputencoding>utf-8</project.reporting.outputencoding>

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

</properties>

<dependencies>

<dependency>

<groupid>org.springframework.boot</groupid>

<artifactid>spring-boot-starter</artifactid>

</dependency>

<dependency>

<groupid>org.springframework.boot</groupid>

<artifactid>spring-boot-starter-thymeleaf</artifactid>

</dependency>

<dependency>

<groupid>org.springframework.boot</groupid>

<artifactid>spring-boot-configuration-processor</artifactid>

<optional>true</optional>

</dependency>

<dependency>

<groupid>org.springframework.boot</groupid>

<artifactid>spring-boot-starter-test</artifactid>

<scope>test</scope>

</dependency>

<!-- https://mvnrepository.com/artifact/org.webjars/bootstrap -->

<dependency>

<groupid>org.webjars</groupid>

<artifactid>bootstrap</artifactid>

<version>3.3.5</version>

</dependency>

<!-- https://mvnrepository.com/artifact/org.webjars.bower/jquery -->

<dependency>

<groupid>org.webjars.bower</groupid>

<artifactid>jquery</artifactid>

<version>2.2.4</version>

</dependency>

</dependencies>

<build>

<plugins>

<plugin>

<groupid>org.springframework.boot</groupid>

<artifactid>spring-boot-maven-plugin</artifactid>

</plugin>

</plugins>

</build>

</project>

可以查看到 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 里配置一个 <multipart-config> ,不过借助于spring-boot的自动配置,你什么都不必做。直接写控制器类,我们在 src/main/java 下新建controller的package,并且新建fileuploadcontroller:

?

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54
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.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 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<linker> 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<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 注解,证明这是一个controller,每个方法前添加了 @getmapping 和 @postmapping 分别相应get和post请求。

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

?

1

2
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提供,我们定义一个接口,包含以下方法:

?

1

2

3

4

5

6

7

8

9

10

11

12

13
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);

stream<path> loadall();

path load(string filename);

resource loadasresource(string filename);

void deleteall();

}

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

?

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

74

75

76

77

78

79

80

81

82

83

84

85

86

87

88

89
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<path> 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提供了很多方便调用的方法让我们可以获取到上传文件的各项信息:

?

1

2

3

4

5

6

7

8

9

10
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. 前端模板

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

?

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69
<html xmlns:th="http://www.thymeleaf.org">

<head>

<title>share files</title>

</head>

<body>

<div class="col-md-8 col-md-offset-2" th:if="${message}">

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

</div>

<div class="col-md-8 col-md-offset-2">

<form method="post" action="/" enctype="multipart/form-data">

<!-- component start -->

<input type="file" name="file" class="input-ghost" style="visibility:hidden; height:0"/>

<div class="form-group">

<div class="input-group input-file" name="fichier1">

<input type="text" class="form-control" placeholder='choose a file...'/>

<span class="input-group-btn">

<button class="btn btn-default btn-choose" type="button">choose</button>

</span>

</div>

</div>

<!-- component end -->

<div class="form-group">

<button type="submit" class="btn btn-primary pull-right">submit</button>

<button type="reset" class="btn btn-danger">reset</button>

</div>

</form>

</div>

<div class="col-md-8 col-md-offset-2">

<ul>

<li th:each="linker: ${linkers}">

<a th:href="${linker.fileurl}" rel="external nofollow" th:text="${linker.filename}" />

</li>

</ul>

</div>

<script src="//ajax.aspnetcdn.com/ajax/jquery/jquery-1.9.1.min.js"></script>

<script src="/webjars/bootstrap/3.3.5/js/bootstrap.min.js"></script>

<script type="text/javascript" th:inline="javascript">

function bs_input_file() {

$(".input-file").before(

function() {

if ( ! $(this).prev().hasclass('input-ghost') ) {

var element = $(".input-ghost");

element.change(function(){

element.next(element).find('input').val((element.val()).split('\\\\').pop());

});

$(this).find("button.btn-choose").click(function(){

element.click();

});

$(this).find("button.btn-reset").click(function(){

element.val(null);

$(this).parents(".input-file").find('input').val('');

});

$(this).find('input').css("cursor","pointer");

$(this).find('input').mousedown(function() {

$(this).parents('.input-file').prev().click();

return false;

});

return element;

}

}

);

}

$(function() {

bs_input_file();

});

</script>

<link rel="stylesheet" href="/webjars/bootstrap/3.3.5/css/bootstrap.min.css" rel="external nofollow" />

</body>

</html>

这里重要的地方还是 <form> 标签内的内容, <form method="post" action="/" enctype="multipart/form-data"> enctype 一定要写成 multipart/form-data ,使用post上传文件,原有的上传控件很丑,所以做了一个text+input放在表面,在下面放了一个隐形的上传文件的input,可以自己看看代码,本文就不啰嗦了。

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

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

其它设置

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

?

1

2
spring.http.multipart.max-file-size=128mb

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

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

?

1

2

3

4

5

6

7

8

9

10

11

12
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的那个类中要添加上

?

1

2

3

4

5

6

7

8
@enableconfigurationproperties注解

@springbootapplication

@enableconfigurationproperties(storageproperties.class)

public class uploadapplication {

public static void main(string[] args) {

springapplication.run(uploadapplication.class, args);

}

}

总结

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

原文链接:https://juejin.im/post/5a326dcaf265da431048685e

收藏 (0) 打赏

感谢您的支持,我会继续努力的!

打开微信/支付宝扫一扫,即可进行扫码打赏哦,分享从这里开始,精彩与您同在
点赞 (0)

声明:本站所有文章,如无特殊说明或标注,均为本站原创发布。任何个人或组织,在未征得本站同意时,禁止复制、盗用、采集、发布本站内容到任何网站、书籍等各类媒体平台。如若本站内容侵犯了原著者的合法权益,可联系我们进行处理。

快网idc优惠网 建站教程 Spring Boot + thymeleaf 实现文件上传下载功能 https://www.kuaiidc.com/76461.html

相关文章

发表评论
暂无评论