基于Spring实现文件上传功能
本小节你将建立一个可以接受http multi-part 文件的服务。
你将建立一个后台服务来接收文件以及前台页面来上传文件。
要利用servlet容器上传文件,你要注册一个multipartconfigelement类,以往需要在web.xml 中配置<multipart-config>,
而在这里,你要感谢springboot,一切都为你自动配置好了。
1、新建一个文件上传的controller:
应用已经包含一些 存储文件 和 从磁盘中加载文件 的类,他们在cn.tiny77.guide05这个包下。我们将会在fileuploadcontroller中用到这些类。
package cn.tiny77.guide05; import java.io.ioexception; import java.util.list; 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; @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<string> paths = storageservice.loadall().map( path -> mvcuricomponentsbuilder.frommethodname(fileuploadcontroller.class, "servefile", path.getfilename().tostring()).build().tostring()) .collect(collectors.tolist()); model.addattribute("files", paths); 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注解将绑定对应的请求参数和请求类型到特定的方法。
get / 通过storageservice 扫描文件列表并 将他们加载到 thymeleaf 模板中。它通过mvcuricomponentsbuilder来生成资源文件的连接地址。
get /files/{filename} 当文件存在时候,将加载文件,并发送文件到浏览器端。通过设置返回头"content-disposition"来实现文件的下载。
post / 接受multi-part文件并将它交给storageservice保存起来。
你需要提供一个服务接口storageservice来帮助controller操作存储层。接口大致如下
package cn.tiny77.guide05; 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(); }
以下是接口实现类
package cn.tiny77.guide05; 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; 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; @service public class filesystemstorageservice implements storageservice { private final path rootlocation; @autowired public filesystemstorageservice(storageproperties properties) { this.rootlocation = paths.get(properties.getlocation()); } @override public void store(multipartfile file) { string filename = stringutils.cleanpath(file.getoriginalfilename()); try { if (file.isempty()) { throw new storageexception("无法保存空文件 " + filename); } if (filename.contains("..")) { // this is a security check throw new storageexception( "无权访问该位置 " + filename); } files.copy(file.getinputstream(), this.rootlocation.resolve(filename), standardcopyoption.replace_existing); } catch (ioexception e) { throw new storageexception("无法保存文件 " + 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("读取文件异常", 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( "无法读取文件: " + filename); } } catch (malformedurlexception e) { throw new storagefilenotfoundexception("无法读取文件: " + filename, e); } } @override public void deleteall() { filesystemutils.deleterecursively(rootlocation.tofile()); } @override public void init() { try { files.createdirectories(rootlocation); } catch (ioexception e) { throw new storageexception("初始化存储空间出错", e); } } }
2、建立一个html页面
这里使用thymeleaf模板
<html xmlns:th="http://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}" rel="external nofollow" th:text="${file}" /> </li> </ul> </div> </body> </html>
页面主要分为三部分分
- 顶部展示springmvc传过来的信息
- 一个提供用户上传文件的表单
- 一个后台提供的文件列表
3、限制上传文件的大小
在文件上传的应用中通常要设置文件大小的,想象一下后台处理的文件如果是5gb,那得多糟糕!在springboot中,我们可以通过属性文件来控制。
新建一个application.properties,代码如下:
spring.http.multipart.max-file-size=128kb #文件总大小不能超过128kb
spring.http.multipart.max-request-size=128kb #请求数据的大小不能超过128kb
4、应用启动函数
package cn.tiny77.guide05; 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; @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(); }; } }
5、运行结果
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持。