时间:2021-05-20
文件的上传及下载功能是开发人员在日常应用及编程开发中经常会遇到的。正好最近开发需要用到此功能,虽然本人是 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,项目创建很简单,按照下面的步骤创建即可:
项目创建完毕之后就可以进行开发,项目的完整结构如下图所示:
参数配置
项目创建完成之后,需要设置一些必要的参数,打开项目resources目录下配置文件application.properties,在其中添加以下参数:
server.port=80## MULTIPART (MultipartProperties)# 开启 multipart 上传功能spring.servlet.multipart.enabled=true# 文件写入磁盘的阈值spring.servlet.multipart.file-size-threshold=2KB# 最大文件大小spring.servlet.multipart.max-file-size=200MB# 最大请求大小spring.servlet.multipart.max-request-size=215MB## 文件存储所需参数# 所有通过 REST APIs 上传的文件都将存储在此目录下file.upload-dir=./uploads其中file.upload-dir=./uploads参数为自定义的参数,创建FileProperties.javaPOJO类,使配置参数可以自动绑定到POJO类。
import org.springframework.boot.context.properties.ConfigurationProperties;@ConfigurationProperties(prefix = "file")public class FileProperties { private String uploadDir; public String getUploadDir() { return uploadDir; } public void setUploadDir(String uploadDir) { this.uploadDir = uploadDir; }}然后在@SpringBootApplication注解的类中添加@EnableConfigurationProperties注解以开启ConfigurationProperties功能。
SpringBootFileApplication.java
@SpringBootApplication@EnableConfigurationProperties({ FileProperties.class})public class SpringBootFileApplication { public static void main(String[] args) { SpringApplication.run(SpringBootFileApplication.class, args); }}配置完成,以后若有file前缀开头的参数需要配置,可直接在application.properties配置文件中配置并更新FileProperties.java即可。
另外再创建一个上传文件成功之后的Response响应实体类UploadFileResponse.java及异常类FileException.java来处理异常信息。
UploadFileResponse.java
public class UploadFileResponse { private String fileName; private String fileDownloadUri; private String fileType; private long size; public UploadFileResponse(String fileName, String fileDownloadUri, String fileType, long size) { this.fileName = fileName; this.fileDownloadUri = fileDownloadUri; this.fileType = fileType; this.size = size; } // getter and setter ...}FileException.java
public class FileException extends RuntimeException{ public FileException(String message) { super(message); } public FileException(String message, Throwable cause) { super(message, cause); }}创建接口
下面需要创建文件上传下载所需的 REST APIs 接口。创建文件FileController.java。
import com.james.sample.file.dto.UploadFileResponse;import com.james.sample.file.service.FileService;import org.slf4j.Logger;import org.slf4j.LoggerFactory;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.core.io.Resource;import org.springframework.http.HttpHeaders;import org.springframework.http.MediaType;import org.springframework.http.ResponseEntity;import org.springframework.web.bind.annotation.*;import org.springframework.web.multipart.MultipartFile;import org.springframework.web.servlet.support.ServletUriComponentsBuilder;import javax.servlet.http.HttpServletRequest;import java.io.IOException;import java.util.Arrays;import java.util.List;import java.util.stream.Collectors;@RestControllerpublic class FileController { private static final Logger logger = LoggerFactory.getLogger(FileController.class); @Autowired private FileService fileService; @PostMapping("/uploadFile") public UploadFileResponse uploadFile(@RequestParam("file") MultipartFile file){ String fileName = fileService.storeFile(file); String fileDownloadUri = ServletUriComponentsBuilder.fromCurrentContextPath() .path("/downloadFile/") .path(fileName) .toUriString(); return new UploadFileResponse(fileName, fileDownloadUri, file.getContentType(), file.getSize()); } @PostMapping("/uploadMultipleFiles") public List<UploadFileResponse> uploadMultipleFiles(@RequestParam("files") MultipartFile[] files) { return Arrays.stream(files) .map(this::uploadFile) .collect(Collectors.toList()); } @GetMapping("/downloadFile/{fileName:.+}") public ResponseEntity<Resource> downloadFile(@PathVariable String fileName, HttpServletRequest request) { // Load file as Resource Resource resource = fileService.loadFileAsResource(fileName); // Try to determine file's content type String contentType = null; try { contentType = request.getServletContext().getMimeType(resource.getFile().getAbsolutePath()); } catch (IOException ex) { logger.info("Could not determine file type."); } // Fallback to the default content type if type could not be determined if(contentType == null) { contentType = "application/octet-stream"; } return ResponseEntity.ok() .contentType(MediaType.parseMediaType(contentType)) .header(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=\"" + resource.getFilename() + "\"") .body(resource); }}FileController类在接收到用户的请求后,使用FileService类提供的storeFile()方法将文件写入到系统中进行存储,其存储目录就是之前在application.properties配置文件中的file.upload-dir参数的值./uploads。
下载接口downloadFile()在接收到用户请求之后,使用FileService类提供的loadFileAsResource()方法获取存储在系统中文件并返回文件供用户下载。
FileService.java
import com.james.sample.file.exception.FileException;import com.james.sample.file.property.FileProperties;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.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;@Servicepublic class FileService { private final Path fileStorageLocation; // 文件在本地存储的地址 @Autowired public FileService(FileProperties fileProperties) { this.fileStorageLocation = Paths.get(fileProperties.getUploadDir()).toAbsolutePath().normalize(); try { Files.createDirectories(this.fileStorageLocation); } catch (Exception ex) { throw new FileException("Could not create the directory where the uploaded files will be stored.", ex); } } /** * 存储文件到系统 * * @param file 文件 * @return 文件名 */ public String storeFile(MultipartFile file) { // Normalize file name String fileName = StringUtils.cleanPath(file.getOriginalFilename()); try { // Check if the file's name contains invalid characters if(fileName.contains("..")) { throw new FileException("Sorry! Filename contains invalid path sequence " + fileName); } // Copy file to the target location (Replacing existing file with the same name) Path targetLocation = this.fileStorageLocation.resolve(fileName); Files.copy(file.getInputStream(), targetLocation, StandardCopyOption.REPLACE_EXISTING); return fileName; } catch (IOException ex) { throw new FileException("Could not store file " + fileName + ". Please try again!", ex); } } /** * 加载文件 * @param fileName 文件名 * @return 文件 */ public Resource loadFileAsResource(String fileName) { try { Path filePath = this.fileStorageLocation.resolve(fileName).normalize(); Resource resource = new UrlResource(filePath.toUri()); if(resource.exists()) { return resource; } else { throw new FileException("File not found " + fileName); } } catch (MalformedURLException ex) { throw new FileException("File not found " + fileName, ex); } }}接口测试
在完成上述的代码之后,打开SpringBootFileApplication.java并运行,运行完成之后就可以使用 Postman 进行测试了。
单个文件上传结果:
多个文件上传结果:
文件下载结果:
Web 前端开发
index.html
<!DOCTYPE html><html lang="zh-cn"><head> <!-- Required meta tags --> <meta charset="UTF-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no"> <title>Spring Boot File Upload / Download Rest API Example</title> <!-- Bootstrap CSS --> <link href="/css/main.css" rel="external nofollow" rel="stylesheet"/></head><body><noscript> <h2>Sorry! Your browser doesn't support Javascript</h2></noscript><div class="upload-container"> <div class="upload-header"> <h2>Spring Boot File Upload / Download Rest API Example</h2> </div> <div class="upload-content"> <div class="single-upload"> <h3>Upload Single File</h3> <form id="singleUploadForm" name="singleUploadForm"> <input id="singleFileUploadInput" type="file" name="file" class="file-input" required/> <button type="submit" class="primary submit-btn">Submit</button> </form> <div class="upload-response"> <div id="singleFileUploadError"></div> <div id="singleFileUploadSuccess"></div> </div> </div> <div class="multiple-upload"> <h3>Upload Multiple Files</h3> <form id="multipleUploadForm" name="multipleUploadForm"> <input id="multipleFileUploadInput" type="file" name="files" class="file-input" multiple required/> <button type="submit" class="primary submit-btn">Submit</button> </form> <div class="upload-response"> <div id="multipleFileUploadError"></div> <div id="multipleFileUploadSuccess"></div> </div> </div> </div></div><!-- Optional JavaScript --><script src="/js/main.js"></script></body></html>main.css
* { -webkit-box-sizing: border-box; -moz-box-sizing: border-box; box-sizing: border-box;}body { margin: 0; padding: 0; font-weight: 400; font-family: "Helvetica Neue", Helvetica, Arial, sans-serif; font-size: 1rem; line-height: 1.58; color: #333; background-color: #f4f4f4;}body:before { height: 50%; width: 100%; position: absolute; top: 0; left: 0; background: #128ff2; content: ""; z-index: 0;}.clearfix:after { display: block; content: ""; clear: both;}h1, h2, h3, h4, h5, h6 { margin-top: 20px; margin-bottom: 20px;}h1 { font-size: 1.7em;}a { color: #128ff2;}button { box-shadow: none; border: 1px solid transparent; font-size: 14px; outline: none; line-height: 100%; white-space: nowrap; vertical-align: middle; padding: 0.6rem 1rem; border-radius: 2px; transition: all 0.2s ease-in-out; cursor: pointer; min-height: 38px;}button.primary { background-color: #128ff2; box-shadow: 0 2px 2px 0 rgba(0, 0, 0, 0.12); color: #fff;}input { font-size: 1rem;}input[type="file"] { border: 1px solid #128ff2; padding: 6px; max-width: 100%;}.file-input { width: 100%;}.submit-btn { display: block; margin-top: 15px; min-width: 100px;}@media screen and (min-width: 500px) { .file-input { width: calc(100% - 115px); } .submit-btn { display: inline-block; margin-top: 0; margin-left: 10px; }}.upload-container { max-width: 700px; margin-left: auto; margin-right: auto; background-color: #fff; box-shadow: 0 1px 11px rgba(0, 0, 0, 0.27); margin-top: 60px; min-height: 400px; position: relative; padding: 20px;}.upload-header { border-bottom: 1px solid #ececec;}.upload-header h2 { font-weight: 500;}.single-upload { padding-bottom: 20px; margin-bottom: 20px; border-bottom: 1px solid #e8e8e8;}.upload-response { overflow-x: hidden; word-break: break-all;}main.js
'use strict';var singleUploadForm = document.querySelector('#singleUploadForm');var singleFileUploadInput = document.querySelector('#singleFileUploadInput');var singleFileUploadError = document.querySelector('#singleFileUploadError');var singleFileUploadSuccess = document.querySelector('#singleFileUploadSuccess');var multipleUploadForm = document.querySelector('#multipleUploadForm');var multipleFileUploadInput = document.querySelector('#multipleFileUploadInput');var multipleFileUploadError = document.querySelector('#multipleFileUploadError');var multipleFileUploadSuccess = document.querySelector('#multipleFileUploadSuccess');function uploadSingleFile(file) { var formData = new FormData(); formData.append("file", file); var xhr = new XMLHttpRequest(); xhr.open("POST", "/uploadFile"); xhr.onload = function() { console.log(xhr.responseText); var response = JSON.parse(xhr.responseText); if(xhr.status == 200) { singleFileUploadError.style.display = "none"; singleFileUploadSuccess.innerHTML = "<p>File Uploaded Successfully.</p><p>DownloadUrl : <a href='" + response.fileDownloadUri + "' target='_blank'>" + response.fileDownloadUri + "</a></p>"; singleFileUploadSuccess.style.display = "block"; } else { singleFileUploadSuccess.style.display = "none"; singleFileUploadError.innerHTML = (response && response.message) || "Some Error Occurred"; } } xhr.send(formData);}function uploadMultipleFiles(files) { var formData = new FormData(); for(var index = 0; index < files.length; index++) { formData.append("files", files[index]); } var xhr = new XMLHttpRequest(); xhr.open("POST", "/uploadMultipleFiles"); xhr.onload = function() { console.log(xhr.responseText); var response = JSON.parse(xhr.responseText); if(xhr.status == 200) { multipleFileUploadError.style.display = "none"; var content = "<p>All Files Uploaded Successfully</p>"; for(var i = 0; i < response.length; i++) { content += "<p>DownloadUrl : <a href='" + response[i].fileDownloadUri + "' target='_blank'>" + response[i].fileDownloadUri + "</a></p>"; } multipleFileUploadSuccess.innerHTML = content; multipleFileUploadSuccess.style.display = "block"; } else { multipleFileUploadSuccess.style.display = "none"; multipleFileUploadError.innerHTML = (response && response.message) || "Some Error Occurred"; } } xhr.send(formData);}singleUploadForm.addEventListener('submit', function(event){ var files = singleFileUploadInput.files; if(files.length === 0) { singleFileUploadError.innerHTML = "Please select a file"; singleFileUploadError.style.display = "block"; } uploadSingleFile(files[0]); event.preventDefault();}, true);multipleUploadForm.addEventListener('submit', function(event){ var files = multipleFileUploadInput.files; if(files.length === 0) { multipleFileUploadError.innerHTML = "Please select at least one file"; multipleFileUploadError.style.display = "block"; } uploadMultipleFiles(files); event.preventDefault();}, true);总结
至此,文件的上传及下载功能已完成。在正式环境中可能还需要将上传的文件存储到数据库,此处按照实际需求去处理即可。
本文源代码地址:https://github.com/JemGeek/SpringBoot-Sample/tree/master/SpringBoot-File
本文参考(需要FQ):https:///spring-boot-file-upload-download-rest-api-example/
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持。
声明:本页内容来源网络,仅供用户参考;我单位不保证亦不表示资料全面及准确无误,也不保证亦不表示这些资料为最新信息,如因任何原因,本网内容或者用户因倚赖本网内容造成任何损失或损害,我单位将不会负任何法律责任。如涉及版权问题,请提交至online#300.cn邮箱联系删除。
SpringBoot对文件上传做了简化,基本做到了零配置,我们只需要在项目中添加spring-boot-starter-web依赖即可。单文件上传1,代码编写(
本文实现springboot的多文件上传,首先创建一个springboot项目,添加spring-boot-starter-web依赖。然后在resources
hdfs文件操作操作示例,包括上传文件到HDFS上、从HDFS上下载文件和删除HDFS上的文件,大家参考使用吧复制代码代码如下:importorg.apache
了解过spring-Boot这个技术的,应该知道Spring-Boot的核心配置文件application.properties,当然也可以通过注解自定义配置文
1pom.xml文件注:热部署功能spring-boot-1.3开始有的org.springframework.bootspring-boot-devtools