Spring Boot 与 Kotlin 上传文件

如果我们做一个小型的web站,而且刚好选择的kotlin 和Spring Boot技术栈,那么上传文件的必不可少了,当然,如果你做一个中大型的web站,那建议你使用云存储,能省不少事情。

这篇文章就介绍怎么使用kotlin 和Spring Boot上传文件

构建工程

如果对于构建工程还不是很熟悉的可以参考《我的第一个Kotlin应用》

完整build.gradle文件

1group 'name.quanke.kotlin' 2version '1.0-SNAPSHOT' 3 4buildscript { 5 ext.kotlin_version = '1.2.10' 6 ext.spring_boot_version = '1.5.4.RELEASE' 7 repositories { 8 mavenCentral() 9 } 10 dependencies { 11 classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" 12 classpath("org.springframework.boot:spring-boot-gradle-plugin:$spring_boot_version") 13 14// Kotlin整合SpringBoot的默认无参构造函数,默认把所有的类设置open类插件 15 classpath("org.jetbrains.kotlin:kotlin-noarg:$kotlin_version") 16 classpath("org.jetbrains.kotlin:kotlin-allopen:$kotlin_version") 17 18 } 19} 20 21apply plugin: 'kotlin' 22apply plugin: "kotlin-spring" // See https://kotlinlang.org/docs/reference/compiler-plugins.html#kotlin-spring-compiler-plugin 23apply plugin: 'org.springframework.boot' 24 25 26jar { 27 baseName = 'chapter11-5-6-service' 28 version = '0.1.0' 29} 30repositories { 31 mavenCentral() 32} 33 34 35dependencies { 36 compile "org.jetbrains.kotlin:kotlin-stdlib-jre8:$kotlin_version" 37 compile "org.springframework.boot:spring-boot-starter-web:$spring_boot_version" 38 compile "org.springframework.boot:spring-boot-starter-thymeleaf:$spring_boot_version" 39 40 testCompile "org.springframework.boot:spring-boot-starter-test:$spring_boot_version" 41 testCompile "org.jetbrains.kotlin:kotlin-test-junit:$kotlin_version" 42 43} 44 45compileKotlin { 46 kotlinOptions.jvmTarget = "1.8" 47} 48compileTestKotlin { 49 kotlinOptions.jvmTarget = "1.8" 50}

创建文件上传controller

1import name.quanke.kotlin.chaper11_5_6.storage.StorageFileNotFoundException 2import name.quanke.kotlin.chaper11_5_6.storage.StorageService 3import org.springframework.beans.factory.annotation.Autowired 4import org.springframework.core.io.Resource 5import org.springframework.http.HttpHeaders 6import org.springframework.http.ResponseEntity 7import org.springframework.stereotype.Controller 8import org.springframework.ui.Model 9import org.springframework.web.bind.annotation.* 10import org.springframework.web.multipart.MultipartFile 11import org.springframework.web.servlet.mvc.method.annotation.MvcUriComponentsBuilder 12import org.springframework.web.servlet.mvc.support.RedirectAttributes 13 14import java.io.IOException 15import java.util.stream.Collectors 16 17 18/** 19 * 文件上传控制器 20 * Created by http://quanke.name on 2018/1/12. 21 */ 22 23@Controller 24class FileUploadController @Autowired 25constructor(private val storageService: StorageService) { 26 27 @GetMapping("/") 28 @Throws(IOException::class) 29 fun listUploadedFiles(model: Model): String { 30 31 model.addAttribute("files", storageService 32 .loadAll() 33 .map { path -> 34 MvcUriComponentsBuilder 35 .fromMethodName(FileUploadController::class.java, "serveFile", path.fileName.toString()) 36 .build().toString() 37 } 38 .collect(Collectors.toList())) 39 40 return "uploadForm" 41 } 42 43 @GetMapping("/files/{filename:.+}") 44 @ResponseBody 45 fun serveFile(@PathVariable filename: String): ResponseEntity<Resource> { 46 47 val file = storageService.loadAsResource(filename) 48 return ResponseEntity 49 .ok() 50 .header(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=\"" + file.filename + "\"") 51 .body(file) 52 } 53 54 @PostMapping("/") 55 fun handleFileUpload(@RequestParam("file") file: MultipartFile, 56 redirectAttributes: RedirectAttributes): String { 57 58 storageService.store(file) 59 redirectAttributes.addFlashAttribute("message", 60 "You successfully uploaded " + file.originalFilename + "!") 61 62 return "redirect:/" 63 } 64 65 @ExceptionHandler(StorageFileNotFoundException::class) 66 fun handleStorageFileNotFound(exc: StorageFileNotFoundException): ResponseEntity<*> { 67 return ResponseEntity.notFound().build<Any>() 68 } 69 70}

上传文件服务的接口

1import org.springframework.core.io.Resource 2import org.springframework.web.multipart.MultipartFile 3 4import java.nio.file.Path 5import java.util.stream.Stream 6 7interface StorageService { 8 9 fun init() 10 11 fun store(file: MultipartFile) 12 13 fun loadAll(): Stream<Path> 14 15 fun load(filename: String): Path 16 17 fun loadAsResource(filename: String): Resource 18 19 fun deleteAll() 20 21}

上传文件服务

1import org.springframework.beans.factory.annotation.Autowired 2import org.springframework.core.io.Resource 3import org.springframework.core.io.UrlResource 4import org.springframework.stereotype.Service 5import org.springframework.util.FileSystemUtils 6import org.springframework.util.StringUtils 7import org.springframework.web.multipart.MultipartFile 8import java.io.IOException 9import java.net.MalformedURLException 10import java.nio.file.Files 11import java.nio.file.Path 12import java.nio.file.Paths 13import java.nio.file.StandardCopyOption 14import java.util.stream.Stream 15 16@Service 17class FileSystemStorageService @Autowired 18constructor(properties: StorageProperties) : StorageService { 19 20 private val rootLocation: Path 21 22 init { 23 this.rootLocation = Paths.get(properties.location) 24 } 25 26 override fun store(file: MultipartFile) { 27 val filename = StringUtils.cleanPath(file.originalFilename) 28 try { 29 if (file.isEmpty) { 30 throw StorageException("Failed to store empty file " + filename) 31 } 32 if (filename.contains("..")) { 33 // This is a security check 34 throw StorageException( 35 "Cannot store file with relative path outside current directory " + filename) 36 } 37 Files.copy(file.inputStream, this.rootLocation.resolve(filename), 38 StandardCopyOption.REPLACE_EXISTING) 39 } catch (e: IOException) { 40 throw StorageException("Failed to store file " + filename, e) 41 } 42 43 } 44 45 override fun loadAll(): Stream<Path> { 46 try { 47 return Files.walk(this.rootLocation, 1) 48 .filter { path -> path != this.rootLocation } 49 .map { path -> this.rootLocation.relativize(path) } 50 } catch (e: IOException) { 51 throw StorageException("Failed to read stored files", e) 52 } 53 54 } 55 56 override fun load(filename: String): Path { 57 return rootLocation.resolve(filename) 58 } 59 60 override fun loadAsResource(filename: String): Resource { 61 try { 62 val file = load(filename) 63 val resource = UrlResource(file.toUri()) 64 return if (resource.exists() || resource.isReadable) { 65 resource 66 } else { 67 throw StorageFileNotFoundException( 68 "Could not read file: " + filename) 69 70 } 71 } catch (e: MalformedURLException) { 72 throw StorageFileNotFoundException("Could not read file: " + filename, e) 73 } 74 75 } 76 77 override fun deleteAll() { 78 FileSystemUtils.deleteRecursively(rootLocation.toFile()) 79 } 80 81 override fun init() { 82 try { 83 Files.createDirectories(rootLocation) 84 } catch (e: IOException) { 85 throw StorageException("Could not initialize storage", e) 86 } 87 88 } 89}

自定义异常

1open class StorageException : RuntimeException { 2 3 constructor(message: String) : super(message) 4 5 constructor(message: String, cause: Throwable) : super(message, cause) 6} 7 8 9class StorageFileNotFoundException : StorageException { 10 11 constructor(message: String) : super(message) 12 13 constructor(message: String, cause: Throwable) : super(message, cause) 14}

配置文件上传目录

1import org.springframework.boot.context.properties.ConfigurationProperties 2 3@ConfigurationProperties("storage") 4class StorageProperties { 5 6 /** 7 * Folder location for storing files 8 */ 9 var location = "upload-dir" 10 11}

启动Spring Boot

1/** 2 * Created by http://quanke.name on 2018/1/9. 3 */ 4 5@SpringBootApplication 6@EnableConfigurationProperties(StorageProperties::class) 7class Application { 8 9 @Bean 10 internal fun init(storageService: StorageService) = CommandLineRunner { 11 storageService.deleteAll() 12 storageService.init() 13 } 14 15 companion object { 16 17 @Throws(Exception::class) 18 @JvmStatic 19 fun main(args: Array<String>) { 20 SpringApplication.run(Application::class.java, *args) 21 } 22 } 23} 24

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

1<html xmlns:th="http://www.thymeleaf.org"> 2<body> 3 4<div th:if="${message}"> 5 <h2 th:text="${message}"/> 6</div> 7 8<div> 9 <form method="POST" enctype="multipart/form-data" action="/"> 10 <table> 11 <tr> 12 <td>File to upload:</td> 13 <td><input type="file" name="file"/></td> 14 </tr> 15 <tr> 16 <td></td> 17 <td><input type="submit" value="Upload"/></td> 18 </tr> 19 </table> 20 </form> 21</div> 22 23<div> 24 <ul> 25 <li th:each="file : ${files}"> 26 <a th:href="${file}" th:text="${file}"/> 27 </li> 28 </ul> 29</div> 30 31</body> 32</html>

配置文件application.yml

1spring: 2 http: 3 multipart: 4 max-file-size: 128KB 5 max-request-size: 128KB 6

更多Spring Boot 和 kotlin相关内容,欢迎关注《Spring Boot 与 kotlin 实战》

源码:

https://github.com/quanke/spring-boot-with-kotlin-in-action/

参考:

全科龙婷

点赞
收藏

评论区

加载中...

相关推荐

MySQL:[Err] 1292 - Incorrect datetime value: ‘0000-00-00 00:00:00‘ for column ‘CREATE_TIME‘ at row 1

文章目录问题用navicat导入数据时,报错:原因这是因为当前的MySQL不支持datetime为0的情况。解决修改sql\mode:sql\mode:SQLMode定义了MySQL应支持的SQL语法、数据校验等,这样可以更容易地在不同的环境中使用MySQL。全局s

Oracle 分组与拼接字符串同时使用

SELECTT.,ROWNUMIDFROM(SELECTT.EMPLID,T.NAME,T.BU,T.REALDEPART,T.FORMATDATE,SUM(T.S0)S0,MAX(UPDATETIME)CREATETIME,LISTAGG(TOCHAR(

MySQL部分从库上面因为大量的临时表tmp_table造成慢查询

背景描述Time:20190124T00:08:14.70572408:00User@Host:@Id:Schema:sentrymetaLast_errno:0Killed:0Query_time:0.315758Lock_

皕杰报表之UUID

​在我们用皕杰报表工具设计填报报表时,如何在新增行里自动增加id呢?能新增整数排序id吗?目前可以在新增行里自动增加id,但只能用uuid函数增加UUID编码,不能新增整数排序id。uuid函数说明:获取一个UUID,可以在填报表中用来创建数据ID语法:uuid()或uuid(sep)参数说明:sep布尔值,生成的uuid中是否包含分隔符'',缺省为

手写Java HashMap源码

HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程22

2020年前端实用代码段,为你的工作保驾护航

有空的时候,自己总结了几个代码段,在开发中也经常使用,谢谢。1、使用解构获取json数据let jsonData  id: 1,status: "OK",data: 'a', 'b';let  id, status, data: number   jsonData;console.log(id, status, number )

Spring Boot 与 Kotlin 上传文件 - HelloWorld