多语言展示
当前在线:1492今日阅读:117今日分享:28

Spring MVC/Spring Boot实现图片文件上传和显示

Spring MVC可以很方便的实现WEB应用的开发,再结合Spring boot就可以实现更加简洁的为web应用提供嵌入式的容器服务,这里介绍使用Spring MVC和Spring boot实现图片文件的上传和显示以及下载功能。
工具/原料
1

Spring框架

2

Jdk 1.8+

3

Eclipse开发工具

4

Maven 3.0

方法/步骤
1

第一步、在eclipse中构建一个标准的Maven工程工程的信息:  fantasy  image

2

为工程添加Spring MVC和Spring boot相关的依赖              org.springframework.boot        spring-boot-starter-parent        1.5.9.RELEASE                            org.springframework.boot            spring-boot-starter-thymeleaf                            org.springframework.boot            spring-boot-devtools            true           

3

创建文件存储服务的接口和实现类,对处理WEB页面的文件上传服务1)文件存储服务接口类:StorageService.javaimport 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 loadAll();    Path load(String filename);    Resource loadAsResource(String filename);    void deleteAll();}2)文件存储服务接口实现类:FileSystemStorageService.javapackage image.storage;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;/** * 文件系统存储服务 * @author fantasy * */@Servicepublic 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('File is empty: ' + filename);            }            if (filename.contains('..')) {                // 文件路径安全校验                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 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());    }    @Override    public void init() {        try {            Files.createDirectories(rootLocation);        }        catch (IOException e) {            throw new StorageException('Could not initialize storage', e);        }    }}3)文件存储处理的异常类StorageException.javapackage image.storage;/** * 文件存储异常运行时处理类 * @author fantasy * */public class StorageException extends RuntimeException { private static final long serialVersionUID = 24302554L; public StorageException(String message) {        super(message);    }    public StorageException(String message, Throwable cause) {        super(message, cause);    }}4) 文件存储异常类:StorageFileNotFoundException.javapackage image.storage;public class StorageFileNotFoundException extends StorageException { private static final long serialVersionUID = -71449580L; public StorageFileNotFoundException(String message) {        super(message);    }    public StorageFileNotFoundException(String message, Throwable cause) {        super(message, cause);    }}

4

创建一个文件存储属性配置类,实现文件存储的相关自定义属性配置,如文件上传的目录名称:StorageProperties.java该类指定了文件上传的目录位置为:upload-dirpackage image.storage;import org.springframework.boot.context.properties.ConfigurationProperties;@ConfigurationProperties('storage')public class StorageProperties {    /**     * Folder location for storing files     */    private String location = 'upload-dir';    public String getLocation() {        return location;    }    public void setLocation(String location) {        this.location = location;    }}

5

实现文件上传控制器,这个是整个WEB应用的的核心,实现WEB页面到文件存储服务的联接控制。文件名:FileUploadController.javapackage image;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.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 image.storage.StorageFileNotFoundException;import image.storage.StorageService;/** * 文件上传服务控制器 * @author Fantasy * */@Controllerpublic class FileUploadController {    private final StorageService storageService;    @Autowired    public FileUploadController(StorageService storageService) {        this.storageService = storageService;    }    //文件上传的url入口:/upload,然后转入templates目录下的uploadForm.html文件    @GetMapping('/upload')    public String showUploadPage(Model model) throws IOException {        return 'uploadForm';    }    //处理post上传文件的url:/upload,处理成功后页面跳转掉“/result”    @PostMapping('/upload')    public String handleFileUpload(@RequestParam('file') MultipartFile file,            RedirectAttributes redirectAttributes) {        storageService.store(file);        redirectAttributes.addFlashAttribute('message',                '文件: ' + file.getOriginalFilename() + '上传成功!');       return 'redirect:/result';    }        //处理url:/result的请求,显示templates目录下的uploadResult.html文件    //显示上传的文件列表    @GetMapping('/result')    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()));                System.out.println(model);        return 'uploadResult';    }        //查看已经上传的文件    @GetMapping('/files/{filename:.+}')    @ResponseBody    public ResponseEntity serveFile(@PathVariable String filename) {            Resource file = storageService.loadAsResource(filename);        return ResponseEntity.ok(file);    }        //图片文件的路径是虚拟路径,如何在页面中直接显示呢?    @GetMapping('img/{filename:.+}')    @ResponseBody    public ResponseEntity getFile(@PathVariable String filename)    {    Resource file = storageService.loadAsResource(filename);        return ResponseEntity.ok(file);    }        @ExceptionHandler(StorageFileNotFoundException.class)    public ResponseEntity handleStorageFileNotFound(StorageFileNotFoundException exc) {        return ResponseEntity.notFound().build();    }}

6

为页面创建静态资源目录,存储静态的html、js,css、图片等文件如在项目中需要用的jquery则需要将jquery的库文件放在工程的静态目录中:E:\workspace\image\src\main\resources\static\js\jquery-3.2.1.min.js在这个工程中我们将index.html和jquery-3.2.1.min.js放在静态文件目录下。

7

为Spring MVC提供thymeleaf模板的WEB页面文件,提供文件上传的页面和文件展现的页面;1)文件上传页面:该页面展示了如何引用静态js文件uploadForm.html

File to upload:
2)文件上传结果展示页面:uploadResult.html注意:上传的文件是图片文件时才可以在页面上正常显示的

10

运行application.java类,启动spring boot应用10:53:55.672 [main] DEBUG org.springframework.boot.devtools.settings.DevToolsSettings - Included patterns for restart : []10:53:55.674 [main] DEBUG org.springframework.boot.devtools.settings.DevToolsSettings - Excluded patterns for restart : [/spring-boot-starter/target/classes/, /spring-boot-autoconfigure/target/classes/, /spring-boot-starter-[\w-]+/, /spring-boot/target/classes/, /spring-boot-actuator/target/classes/, /spring-boot-devtools/target/classes/]10:53:55.674 [main] DEBUG org.springframework.boot.devtools.restart.ChangeableUrls - Matching URLs for reloading : [file:/E:/workspace/image/target/classes/]  .   ____          _            __ _ _ /\\ / ___'_ __ _ _(_)_ __  __ _ \ \ \ \( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \ \\/  ___)| |_)| | | | | || (_| |  ) ) ) )  '  |____| .__|_| |_|_| |_\__, | / / / / =========|_|==============|___/=/_/_/_/ :: Spring Boot ::        (v1.5.9.RELEASE)2018-01-07 10:53:55.950  INFO 2932 --- [  restartedMain] image.Application                        : Starting Application on V3EQ67T1ANYZUSN with PID 2932 (E:\workspace\image\target\classes started by Administrator in E:\workspace\image)2018-01-07 10:53:55.950  INFO 2932 --- [  restartedMain] image.Application                        : No active profile set, falling back to default profiles: default2018-01-07 10:53:56.193  INFO 2932 --- [  restartedMain] ationConfigEmbeddedWebApplicationContext : Refreshing org.springframework.boot.context.embedded.AnnotationConfigEmbeddedWebApplicationContext@1f8de44: startup date [Sun Jan 07 10:53:56 CST 2018]; root of context hierarchy2018-01-07 10:53:57.719  INFO 2932 --- [  restartedMain] s.b.c.e.t.TomcatEmbeddedServletContainer : Tomcat initialized with port(s): 8080 (http)2018-01-07 10:53:57.734  INFO 2932 --- [  restartedMain] o.apache.catalina.core.StandardService   : Starting service [Tomcat]2018-01-07 10:53:57.736  INFO 2932 --- [  restartedMain] org.apache.catalina.core.StandardEngine  : Starting Servlet Engine: Apache Tomcat/8.5.232018-01-07 10:53:57.862  INFO 2932 --- [ost-startStop-1] o.a.c.c.C.[Tomcat].[localhost].[/]       : Initializing Spring embedded WebApplicationContext2018-01-07 10:53:57.862  INFO 2932 --- [ost-startStop-1] o.s.web.context.ContextLoader            : Root WebApplicationContext: initialization completed in 1670 ms2018-01-07 10:53:58.020  INFO 2932 --- [ost-startStop-1] o.s.b.w.servlet.ServletRegistrationBean  : Mapping servlet: 'dispatcherServlet' to [/]2018-01-07 10:53:58.025  INFO 2932 --- [ost-startStop-1] o.s.b.w.servlet.FilterRegistrationBean   : Mapping filter: 'characterEncodingFilter' to: [/*]2018-01-07 10:53:58.025  INFO 2932 --- [ost-startStop-1] o.s.b.w.servlet.FilterRegistrationBean   : Mapping filter: 'hiddenHttpMethodFilter' to: [/*]2018-01-07 10:53:58.026  INFO 2932 --- [ost-startStop-1] o.s.b.w.servlet.FilterRegistrationBean   : Mapping filter: 'httpPutFormContentFilter' to: [/*]2018-01-07 10:53:58.026  INFO 2932 --- [ost-startStop-1] o.s.b.w.servlet.FilterRegistrationBean   : Mapping filter: 'requestContextFilter' to: [/*]2018-01-07 10:53:58.361  INFO 2932 --- [  restartedMain] s.w.s.m.m.a.RequestMappingHandlerAdapter : Looking for @ControllerAdvice: org.springframework.boot.context.embedded.AnnotationConfigEmbeddedWebApplicationContext@1f8de44: startup date [Sun Jan 07 10:53:56 CST 2018]; root of context hierarchy2018-01-07 10:53:58.435  INFO 2932 --- [  restartedMain] s.w.s.m.m.a.RequestMappingHandlerMapping : Mapped '{[/img/{filename:.+}],methods=[GET]}' onto public org.springframework.http.ResponseEntity image.FileUploadController.getFile(java.lang.String)2018-01-07 10:53:58.436  INFO 2932 --- [  restartedMain] s.w.s.m.m.a.RequestMappingHandlerMapping : Mapped '{[/upload],methods=[GET]}' onto public java.lang.String image.FileUploadController.showUploadPage(org.springframework.ui.Model) throws java.io.IOException2018-01-07 10:53:58.440  INFO 2932 --- [  restartedMain] s.w.s.m.m.a.RequestMappingHandlerMapping : Mapped '{[/upload],methods=[POST]}' onto public java.lang.String image.FileUploadController.handleFileUpload(org.springframework.web.multipart.MultipartFile,org.springframework.web.servlet.mvc.support.RedirectAttributes)2018-01-07 10:53:58.440  INFO 2932 --- [  restartedMain] s.w.s.m.m.a.RequestMappingHandlerMapping : Mapped '{[/files/{filename:.+}],methods=[GET]}' onto public org.springframework.http.ResponseEntity image.FileUploadController.serveFile(java.lang.String)2018-01-07 10:53:58.441  INFO 2932 --- [  restartedMain] s.w.s.m.m.a.RequestMappingHandlerMapping : Mapped '{[/result],methods=[GET]}' onto public java.lang.String image.FileUploadController.listUploadedFiles(org.springframework.ui.Model) throws java.io.IOException2018-01-07 10:53:58.443  INFO 2932 --- [  restartedMain] s.w.s.m.m.a.RequestMappingHandlerMapping : Mapped '{[/error]}' onto public org.springframework.http.ResponseEntity> org.springframework.boot.autoconfigure.web.BasicErrorController.error(javax.servlet.http.HttpServletRequest)2018-01-07 10:53:58.443  INFO 2932 --- [  restartedMain] s.w.s.m.m.a.RequestMappingHandlerMapping : Mapped '{[/error],produces=[text/html]}' onto public org.springframework.web.servlet.ModelAndView org.springframework.boot.autoconfigure.web.BasicErrorController.errorHtml(javax.servlet.http.HttpServletRequest,javax.servlet.http.HttpServletResponse)2018-01-07 10:53:58.473  INFO 2932 --- [  restartedMain] o.s.w.s.handler.SimpleUrlHandlerMapping  : Mapped URL path [/webjars/**] onto handler of type [class org.springframework.web.servlet.resource.ResourceHttpRequestHandler]2018-01-07 10:53:58.473  INFO 2932 --- [  restartedMain] o.s.w.s.handler.SimpleUrlHandlerMapping  : Mapped URL path [/**] onto handler of type [class org.springframework.web.servlet.resource.ResourceHttpRequestHandler]2018-01-07 10:53:58.516  INFO 2932 --- [  restartedMain] o.s.w.s.handler.SimpleUrlHandlerMapping  : Mapped URL path [/**/favicon.ico] onto handler of type [class org.springframework.web.servlet.resource.ResourceHttpRequestHandler]2018-01-07 10:53:59.020  INFO 2932 --- [  restartedMain] oConfiguration$WelcomePageHandlerMapping : Adding welcome page: class path resource [static/index.html]2018-01-07 10:53:59.241  INFO 2932 --- [  restartedMain] o.s.b.d.a.OptionalLiveReloadServer       : LiveReload server is running on port 357292018-01-07 10:53:59.328  INFO 2932 --- [  restartedMain] o.s.j.e.a.AnnotationMBeanExporter        : Registering beans for JMX exposure on startup2018-01-07 10:53:59.419  INFO 2932 --- [  restartedMain] s.b.c.e.t.TomcatEmbeddedServletContainer : Tomcat started on port(s): 8080 (http)2018-01-07 10:53:59.426  INFO 2932 --- [  restartedMain] image.Application                        : Started Application in 3.737 seconds (JVM running for 4.758)

推荐信息