经验首页 前端设计 程序设计 Java相关 移动开发 数据库/运维 软件/图像 大数据/云计算 其他经验
当前位置:技术经验 » Java相关 » Spring Boot » 查看文章
Spring Boot 文件上传与下载的示例代码
来源:jb51  时间:2019/4/1 8:48:46  对本文有异议

文件的上传及下载功能是开发人员在日常应用及编程开发中经常会遇到的。正好最近开发需要用到此功能,虽然本人是 Android 开发人员,但还是业余客串了一下后台开发。

在本文中,您将学习如何使用 Spring Boot 实现 Web 服务中的文件上传和下载功能。首先会构建一个 REST APIs 实现上传及下载的功能,然后使用 Postman 工具来测试这些接口,最后创建一个 Web 界面使用 JavaScript 调用接口演示完整的功能。最终界面及功能如下:

项目环境

- Spring Boot : 2.1.3.RELEASE
- Gredle : 5.2.1
- Java : 1.8
- Intellij IDEA : 2018.3.3

项目创建

开发环境为 Intellij IDEA,项目创建很简单,按照下面的步骤创建即可:

  1. File -> New -> Project...
  2. 选择 Spring Initializr,点击 Next
  3. 填写 Group (项目域名) 和 Artifact (项目别名)
  4. 构建类型可以选择 Maven 或 Gradle, 看个人习惯
  5. 添加 Web 依赖
  6. 输入项目名称及保存路径,完成创建

项目创建完毕之后就可以进行开发,项目的完整结构如下图所示:

参数配置

项目创建完成之后,需要设置一些必要的参数,打开项目resources目录下配置文件application.properties,在其中添加以下参数:

  1. server.port=80
  2.  
  3. ## MULTIPART (MultipartProperties)
  4. # 开启 multipart 上传功能
  5. spring.servlet.multipart.enabled=true
  6. # 文件写入磁盘的阈值
  7. spring.servlet.multipart.file-size-threshold=2KB
  8. # 最大文件大小
  9. spring.servlet.multipart.max-file-size=200MB
  10. # 最大请求大小
  11. spring.servlet.multipart.max-request-size=215MB
  12.  
  13. ## 文件存储所需参数
  14. # 所有通过 REST APIs 上传的文件都将存储在此目录下
  15. file.upload-dir=./uploads

其中file.upload-dir=./uploads参数为自定义的参数,创建FileProperties.javaPOJO类,使配置参数可以自动绑定到POJO类。

  1. import org.springframework.boot.context.properties.ConfigurationProperties;
  2.  
  3. @ConfigurationProperties(prefix = "file")
  4. public class FileProperties {
  5. private String uploadDir;
  6.  
  7. public String getUploadDir() {
  8. return uploadDir;
  9. }
  10. public void setUploadDir(String uploadDir) {
  11. this.uploadDir = uploadDir;
  12. }
  13. }

然后在@SpringBootApplication注解的类中添加@EnableConfigurationProperties注解以开启ConfigurationProperties功能。

SpringBootFileApplication.java

  1. @SpringBootApplication
  2. @EnableConfigurationProperties({
  3. FileProperties.class
  4. })
  5. public class SpringBootFileApplication {
  6.  
  7. public static void main(String[] args) {
  8. SpringApplication.run(SpringBootFileApplication.class, args);
  9. }
  10. }

配置完成,以后若有file前缀开头的参数需要配置,可直接在application.properties配置文件中配置并更新FileProperties.java即可。

另外再创建一个上传文件成功之后的Response响应实体类UploadFileResponse.java及异常类FileException.java来处理异常信息。

UploadFileResponse.java

  1. public class UploadFileResponse {
  2. private String fileName;
  3. private String fileDownloadUri;
  4. private String fileType;
  5. private long size;
  6.  
  7. public UploadFileResponse(String fileName, String fileDownloadUri, String fileType, long size) {
  8. this.fileName = fileName;
  9. this.fileDownloadUri = fileDownloadUri;
  10. this.fileType = fileType;
  11. this.size = size;
  12. }
  13. // getter and setter ...
  14. }

FileException.java

  1. public class FileException extends RuntimeException{
  2. public FileException(String message) {
  3. super(message);
  4. }
  5.  
  6. public FileException(String message, Throwable cause) {
  7. super(message, cause);
  8. }
  9. }

创建接口

下面需要创建文件上传下载所需的 REST APIs 接口。创建文件FileController.java

  1. import com.james.sample.file.dto.UploadFileResponse;
  2. import com.james.sample.file.service.FileService;
  3. import org.slf4j.Logger;
  4. import org.slf4j.LoggerFactory;
  5. import org.springframework.beans.factory.annotation.Autowired;
  6. import org.springframework.core.io.Resource;
  7. import org.springframework.http.HttpHeaders;
  8. import org.springframework.http.MediaType;
  9. import org.springframework.http.ResponseEntity;
  10. import org.springframework.web.bind.annotation.*;
  11. import org.springframework.web.multipart.MultipartFile;
  12. import org.springframework.web.servlet.support.ServletUriComponentsBuilder;
  13.  
  14. import javax.servlet.http.HttpServletRequest;
  15. import java.io.IOException;
  16. import java.util.Arrays;
  17. import java.util.List;
  18. import java.util.stream.Collectors;
  19.  
  20. @RestController
  21. public class FileController {
  22.  
  23. private static final Logger logger = LoggerFactory.getLogger(FileController.class);
  24.  
  25. @Autowired
  26. private FileService fileService;
  27.  
  28. @PostMapping("/uploadFile")
  29. public UploadFileResponse uploadFile(@RequestParam("file") MultipartFile file){
  30. String fileName = fileService.storeFile(file);
  31.  
  32. String fileDownloadUri = ServletUriComponentsBuilder.fromCurrentContextPath()
  33. .path("/downloadFile/")
  34. .path(fileName)
  35. .toUriString();
  36.  
  37. return new UploadFileResponse(fileName, fileDownloadUri,
  38. file.getContentType(), file.getSize());
  39. }
  40.  
  41.  
  42. @PostMapping("/uploadMultipleFiles")
  43. public List<UploadFileResponse> uploadMultipleFiles(@RequestParam("files") MultipartFile[] files) {
  44. return Arrays.stream(files)
  45. .map(this::uploadFile)
  46. .collect(Collectors.toList());
  47. }
  48.  
  49. @GetMapping("/downloadFile/{fileName:.+}")
  50. public ResponseEntity<Resource> downloadFile(@PathVariable String fileName, HttpServletRequest request) {
  51. // Load file as Resource
  52. Resource resource = fileService.loadFileAsResource(fileName);
  53.  
  54. // Try to determine file's content type
  55. String contentType = null;
  56. try {
  57. contentType = request.getServletContext().getMimeType(resource.getFile().getAbsolutePath());
  58. } catch (IOException ex) {
  59. logger.info("Could not determine file type.");
  60. }
  61.  
  62. // Fallback to the default content type if type could not be determined
  63. if(contentType == null) {
  64. contentType = "application/octet-stream";
  65. }
  66.  
  67. return ResponseEntity.ok()
  68. .contentType(MediaType.parseMediaType(contentType))
  69. .header(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=\"" + resource.getFilename() + "\"")
  70. .body(resource);
  71. }
  72. }

FileController类在接收到用户的请求后,使用FileService类提供的storeFile()方法将文件写入到系统中进行存储,其存储目录就是之前在application.properties配置文件中的file.upload-dir参数的值./uploads

下载接口downloadFile()在接收到用户请求之后,使用FileService类提供的loadFileAsResource()方法获取存储在系统中文件并返回文件供用户下载。

FileService.java

  1. import com.james.sample.file.exception.FileException;
  2. import com.james.sample.file.property.FileProperties;
  3. import org.springframework.beans.factory.annotation.Autowired;
  4. import org.springframework.core.io.Resource;
  5. import org.springframework.core.io.UrlResource;
  6. import org.springframework.stereotype.Service;
  7. import org.springframework.util.StringUtils;
  8. import org.springframework.web.multipart.MultipartFile;
  9.  
  10. import java.io.IOException;
  11. import java.net.MalformedURLException;
  12. import java.nio.file.Files;
  13. import java.nio.file.Path;
  14. import java.nio.file.Paths;
  15. import java.nio.file.StandardCopyOption;
  16.  
  17. @Service
  18. public class FileService {
  19.  
  20. private final Path fileStorageLocation; // 文件在本地存储的地址
  21.  
  22. @Autowired
  23. public FileService(FileProperties fileProperties) {
  24. this.fileStorageLocation = Paths.get(fileProperties.getUploadDir()).toAbsolutePath().normalize();
  25. try {
  26. Files.createDirectories(this.fileStorageLocation);
  27. } catch (Exception ex) {
  28. throw new FileException("Could not create the directory where the uploaded files will be stored.", ex);
  29. }
  30. }
  31.  
  32. /**
  33. * 存储文件到系统
  34. *
  35. * @param file 文件
  36. * @return 文件名
  37. */
  38. public String storeFile(MultipartFile file) {
  39. // Normalize file name
  40. String fileName = StringUtils.cleanPath(file.getOriginalFilename());
  41.  
  42. try {
  43. // Check if the file's name contains invalid characters
  44. if(fileName.contains("..")) {
  45. throw new FileException("Sorry! Filename contains invalid path sequence " + fileName);
  46. }
  47.  
  48. // Copy file to the target location (Replacing existing file with the same name)
  49. Path targetLocation = this.fileStorageLocation.resolve(fileName);
  50. Files.copy(file.getInputStream(), targetLocation, StandardCopyOption.REPLACE_EXISTING);
  51.  
  52. return fileName;
  53. } catch (IOException ex) {
  54. throw new FileException("Could not store file " + fileName + ". Please try again!", ex);
  55. }
  56. }
  57.  
  58. /**
  59. * 加载文件
  60. * @param fileName 文件名
  61. * @return 文件
  62. */
  63. public Resource loadFileAsResource(String fileName) {
  64. try {
  65. Path filePath = this.fileStorageLocation.resolve(fileName).normalize();
  66. Resource resource = new UrlResource(filePath.toUri());
  67. if(resource.exists()) {
  68. return resource;
  69. } else {
  70. throw new FileException("File not found " + fileName);
  71. }
  72. } catch (MalformedURLException ex) {
  73. throw new FileException("File not found " + fileName, ex);
  74. }
  75. }
  76. }

接口测试

在完成上述的代码之后,打开SpringBootFileApplication.java并运行,运行完成之后就可以使用 Postman 进行测试了。

单个文件上传结果:

多个文件上传结果:

文件下载结果:

Web 前端开发

index.html

  1. <!DOCTYPE html>
  2. <html lang="zh-cn">
  3. <head>
  4. <!-- Required meta tags -->
  5. <meta charset="UTF-8">
  6. <meta http-equiv="X-UA-Compatible" content="IE=edge">
  7. <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
  8. <title>Spring Boot File Upload / Download Rest API Example</title>
  9.  
  10. <!-- Bootstrap CSS -->
  11. <link href="/css/main.css" rel="external nofollow" rel="stylesheet"/>
  12. </head>
  13. <body>
  14.  
  15. <noscript>
  16. <h2>Sorry! Your browser doesn't support Javascript</h2>
  17. </noscript>
  18.  
  19. <div class="upload-container">
  20. <div class="upload-header">
  21. <h2>Spring Boot File Upload / Download Rest API Example</h2>
  22. </div>
  23. <div class="upload-content">
  24. <div class="single-upload">
  25. <h3>Upload Single File</h3>
  26. <form id="singleUploadForm" name="singleUploadForm">
  27. <input id="singleFileUploadInput" type="file" name="file" class="file-input" required/>
  28. <button type="submit" class="primary submit-btn">Submit</button>
  29. </form>
  30. <div class="upload-response">
  31. <div id="singleFileUploadError"></div>
  32. <div id="singleFileUploadSuccess"></div>
  33. </div>
  34. </div>
  35. <div class="multiple-upload">
  36. <h3>Upload Multiple Files</h3>
  37. <form id="multipleUploadForm" name="multipleUploadForm">
  38. <input id="multipleFileUploadInput" type="file" name="files" class="file-input" multiple required/>
  39. <button type="submit" class="primary submit-btn">Submit</button>
  40. </form>
  41. <div class="upload-response">
  42. <div id="multipleFileUploadError"></div>
  43. <div id="multipleFileUploadSuccess"></div>
  44. </div>
  45. </div>
  46. </div>
  47. </div>
  48.  
  49. <!-- Optional JavaScript -->
  50. <script src="/js/main.js"></script>
  51. </body>
  52. </html>

main.css

  1. * {
  2. -webkit-box-sizing: border-box;
  3. -moz-box-sizing: border-box;
  4. box-sizing: border-box;
  5. }
  6.  
  7. body {
  8. margin: 0;
  9. padding: 0;
  10. font-weight: 400;
  11. font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
  12. font-size: 1rem;
  13. line-height: 1.58;
  14. color: #333;
  15. background-color: #f4f4f4;
  16. }
  17.  
  18. body:before {
  19. height: 50%;
  20. width: 100%;
  21. position: absolute;
  22. top: 0;
  23. left: 0;
  24. background: #128ff2;
  25. content: "";
  26. z-index: 0;
  27. }
  28.  
  29. .clearfix:after {
  30. display: block;
  31. content: "";
  32. clear: both;
  33. }
  34.  
  35.  
  36. h1, h2, h3, h4, h5, h6 {
  37. margin-top: 20px;
  38. margin-bottom: 20px;
  39. }
  40.  
  41. h1 {
  42. font-size: 1.7em;
  43. }
  44.  
  45. a {
  46. color: #128ff2;
  47. }
  48.  
  49. button {
  50. box-shadow: none;
  51. border: 1px solid transparent;
  52. font-size: 14px;
  53. outline: none;
  54. line-height: 100%;
  55. white-space: nowrap;
  56. vertical-align: middle;
  57. padding: 0.6rem 1rem;
  58. border-radius: 2px;
  59. transition: all 0.2s ease-in-out;
  60. cursor: pointer;
  61. min-height: 38px;
  62. }
  63.  
  64. button.primary {
  65. background-color: #128ff2;
  66. box-shadow: 0 2px 2px 0 rgba(0, 0, 0, 0.12);
  67. color: #fff;
  68. }
  69.  
  70. input {
  71. font-size: 1rem;
  72. }
  73.  
  74. input[type="file"] {
  75. border: 1px solid #128ff2;
  76. padding: 6px;
  77. max-width: 100%;
  78. }
  79.  
  80. .file-input {
  81. width: 100%;
  82. }
  83.  
  84. .submit-btn {
  85. display: block;
  86. margin-top: 15px;
  87. min-width: 100px;
  88. }
  89.  
  90. @media screen and (min-width: 500px) {
  91. .file-input {
  92. width: calc(100% - 115px);
  93. }
  94.  
  95. .submit-btn {
  96. display: inline-block;
  97. margin-top: 0;
  98. margin-left: 10px;
  99. }
  100. }
  101.  
  102. .upload-container {
  103. max-width: 700px;
  104. margin-left: auto;
  105. margin-right: auto;
  106. background-color: #fff;
  107. box-shadow: 0 1px 11px rgba(0, 0, 0, 0.27);
  108. margin-top: 60px;
  109. min-height: 400px;
  110. position: relative;
  111. padding: 20px;
  112. }
  113.  
  114. .upload-header {
  115. border-bottom: 1px solid #ececec;
  116. }
  117.  
  118. .upload-header h2 {
  119. font-weight: 500;
  120. }
  121.  
  122. .single-upload {
  123. padding-bottom: 20px;
  124. margin-bottom: 20px;
  125. border-bottom: 1px solid #e8e8e8;
  126. }
  127.  
  128. .upload-response {
  129. overflow-x: hidden;
  130. word-break: break-all;
  131. }

main.js

  1. 'use strict';
  2.  
  3. var singleUploadForm = document.querySelector('#singleUploadForm');
  4. var singleFileUploadInput = document.querySelector('#singleFileUploadInput');
  5. var singleFileUploadError = document.querySelector('#singleFileUploadError');
  6. var singleFileUploadSuccess = document.querySelector('#singleFileUploadSuccess');
  7.  
  8. var multipleUploadForm = document.querySelector('#multipleUploadForm');
  9. var multipleFileUploadInput = document.querySelector('#multipleFileUploadInput');
  10. var multipleFileUploadError = document.querySelector('#multipleFileUploadError');
  11. var multipleFileUploadSuccess = document.querySelector('#multipleFileUploadSuccess');
  12.  
  13. function uploadSingleFile(file) {
  14. var formData = new FormData();
  15. formData.append("file", file);
  16.  
  17. var xhr = new XMLHttpRequest();
  18. xhr.open("POST", "/uploadFile");
  19.  
  20. xhr.onload = function() {
  21. console.log(xhr.responseText);
  22. var response = JSON.parse(xhr.responseText);
  23. if(xhr.status == 200) {
  24. singleFileUploadError.style.display = "none";
  25. singleFileUploadSuccess.innerHTML = "<p>File Uploaded Successfully.</p><p>DownloadUrl : <a href='" + response.fileDownloadUri + "' target='_blank'>" + response.fileDownloadUri + "</a></p>";
  26. singleFileUploadSuccess.style.display = "block";
  27. } else {
  28. singleFileUploadSuccess.style.display = "none";
  29. singleFileUploadError.innerHTML = (response && response.message) || "Some Error Occurred";
  30. }
  31. }
  32.  
  33. xhr.send(formData);
  34. }
  35.  
  36. function uploadMultipleFiles(files) {
  37. var formData = new FormData();
  38. for(var index = 0; index < files.length; index++) {
  39. formData.append("files", files[index]);
  40. }
  41.  
  42. var xhr = new XMLHttpRequest();
  43. xhr.open("POST", "/uploadMultipleFiles");
  44.  
  45. xhr.onload = function() {
  46. console.log(xhr.responseText);
  47. var response = JSON.parse(xhr.responseText);
  48. if(xhr.status == 200) {
  49. multipleFileUploadError.style.display = "none";
  50. var content = "<p>All Files Uploaded Successfully</p>";
  51. for(var i = 0; i < response.length; i++) {
  52. content += "<p>DownloadUrl : <a href='" + response[i].fileDownloadUri + "' target='_blank'>" + response[i].fileDownloadUri + "</a></p>";
  53. }
  54. multipleFileUploadSuccess.innerHTML = content;
  55. multipleFileUploadSuccess.style.display = "block";
  56. } else {
  57. multipleFileUploadSuccess.style.display = "none";
  58. multipleFileUploadError.innerHTML = (response && response.message) || "Some Error Occurred";
  59. }
  60. }
  61.  
  62. xhr.send(formData);
  63. }
  64.  
  65. singleUploadForm.addEventListener('submit', function(event){
  66. var files = singleFileUploadInput.files;
  67. if(files.length === 0) {
  68. singleFileUploadError.innerHTML = "Please select a file";
  69. singleFileUploadError.style.display = "block";
  70. }
  71. uploadSingleFile(files[0]);
  72. event.preventDefault();
  73. }, true);
  74.  
  75. multipleUploadForm.addEventListener('submit', function(event){
  76. var files = multipleFileUploadInput.files;
  77. if(files.length === 0) {
  78. multipleFileUploadError.innerHTML = "Please select at least one file";
  79. multipleFileUploadError.style.display = "block";
  80. }
  81. uploadMultipleFiles(files);
  82. event.preventDefault();
  83. }, true);

总结

至此,文件的上传及下载功能已完成。在正式环境中可能还需要将上传的文件存储到数据库,此处按照实际需求去处理即可。

本文源代码地址:https://github.com/JemGeek/SpringBoot-Sample/tree/master/SpringBoot-File

本文参考(需要FQ):https://www.callicoder.com/spring-boot-file-upload-download-rest-api-example/

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持w3xue。

 友情链接:直通硅谷  点职佳  北美留学生论坛

本站QQ群:前端 618073944 | Java 606181507 | Python 626812652 | C/C++ 612253063 | 微信 634508462 | 苹果 692586424 | C#/.net 182808419 | PHP 305140648 | 运维 608723728

W3xue 的所有内容仅供测试,对任何法律问题及风险不承担任何责任。通过使用本站内容随之而来的风险与本站无关。
关于我们  |  意见建议  |  捐助我们  |  报错有奖  |  广告合作、友情链接(目前9元/月)请联系QQ:27243702 沸活量
皖ICP备17017327号-2 皖公网安备34020702000426号