Java中的流
对输入输出设备的一种抽象理解,在java中,对数据的输入输出操作都是以“流”的方式进行的。
“流”具有方向性,输入流、输出流是相对的。
当程序需要从数据源中读入数据的时候就会开启一个输入流,
相反,写出数据到某个数据源目的地的时候也会开启一个输出流。
数据源可以是文件、内存或者网络等。
MultipartFile类—处理文件上传的文件
是SpringMVC提供简化上传操作的工具类。
在不使用框架之前,都是使用原生的HttpServletRequest来接收上传的数据,文件是以二进制流传递到后端的,然后需要我们自己转换为File类。
MultipartFile主要是用表单的形式进行文件上传,
MultipartFile
的作用
MultipartFile
是 Spring对客户端通过对 HTTPmultipart/form-data
请求中文件数据的封装。它提供了以下功能:
-
在接收到文件时,可以获取文件的相关属性,比如文件名、文件大小、文件类型等等。
-
读取文件内容为字节数组或输入流。
-
将文件保存到磁盘。
MultipartFile
是 Spring 框架中用于处理文件上传的核心接口,并提供了一系列方法用于操作这些文件数据(如获取文件名、文件内容、保存文件等)
//基本信息方法
//返回参数的名称
//获取表单字段的名称(即 <input type="file" name="file"> 中的 name)
String getName();
//获取源文件的昵称 获取客户端上传的文件名(如 example.txt)
String getOriginalFilename();
//返回文件的内容类型 获取文件的 MIME 类型(如 text/plain)
String getContentType();
//文件内容访问方法
//判断是否为空,或者上传的文件是否有内容
boolean isEmpty();
//获取文件大小(以字节为单位)
long getSize();
//将文件内容读取为字节数组。 将文件内容转化成一个byte[] 返回
byte[] getBytes() throws IOException;
//获取文件的输入流,用于读取文件内容 返回InputStream读取文件的内容
InputStream getInputStream() throws IOException;
//文件存储方法
//将上传的文件保存到指定文件系统中
void transferTo(File dest)
//例如
File dest = new File("/path/to/save/" + file.getOriginalFilename());
file.transferTo(dest);
//用于将文件内容传输到指定的 Path 对象中,将上传的文件保存到指定Path路径。
transferTo(Path dest)
//例如
Path path = Paths.get("/path/to/save", file.getOriginalFilename());
file.transferTo(path);
//资源转换方法
// 将MultipartFile转换为Resource对象
getResource()
//例如
Resource resource = file.getResource()
核心组件
-
@RequestParam("file") MultipartFile file
: 从请求中提取名为file
的文件参数。MultipartFile
是 Spring 提供的用于处理文件上传的接口。 -
ResponseEntity<String>
: 用于封装 HTTP 响应,包括状态码和响应体(这里是字符串)。
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile;
import java.io.File;
import java.io.IOException;
@RestController
public class FileUploadController {
private static final String UPLOAD_DIR = "uploads";
@PostMapping("/upload")
public ResponseEntity<String> uploadFile(@RequestParam("file") MultipartFile file) {
//检查文件是否为空 如果客户端上传的文件为空,返回 400 Bad Request 状态码,并提示文件为空
if (file.isEmpty()) {
return ResponseEntity.badRequest().body("File is empty.");
}
try {
//设置上传目录
//获取当前工作目录(System.getProperty("user.dir")),并拼接上传目录 uploads。
//如果上传目录不存在,则创建该目录
String uploadPath = System.getProperty("user.dir") + File.separator + UPLOAD_DIR;
File uploadDir = new File(uploadPath);
if (!uploadDir.exists()) {
uploadDir.mkdir();
}
//保存文件到服务器
//构建文件的完整路径(uploadPath + 文件名)。
//使用 MultipartFile.transferTo() 方法将文件保存到指定路径
String filePath = uploadPath + File.separator + file.getOriginalFilename();
file.transferTo(new File(filePath));
//如果文件保存成功,返回 200 OK 状态码,并提示文件上传成功
return ResponseEntity.ok("File uploaded successfully: " + file.getOriginalFilename());
} catch (IOException e) {
return ResponseEntity.internalServerError().body("Failed to upload file: " + e.getMessage());
}
}
}
MultipartFile
是 Spring 对 HTTP 文件上传的封装,提供了简单易用的 API 用于操作文件数据。它的底层实现依赖于 MultipartResolver
,支持内存存储和临时文件存储两种方式。通过合理配置和使用,可以高效、安全地处理文件上传功能。
示例代码
private static final String fileDirectory = "D:\\文件\\scriptFiles";
@Override
public R<?> addScriptFile(MultipartFile file) {
try {
// 校验文件类型
if (!file.getOriginalFilename().endsWith(".py")) {
return R.Failed("只支持 .py 文件上传");
}
// 生成唯一文件名
String fileName = UUID.randomUUID().toString() + "-" + file.getOriginalFilename();
// 尝试从本地文件系统加载文件
Path filePath = Paths.get(fileDirectory).normalize();
//将文件路径转换为 URI,然后通过 UrlResource 创建文件资源对象
Resource resource = new UrlResource(filePath.toUri());
//resource.getFile():转换为 File 对象,表示目标目录的物理文件路径。
// staticDir 将指向该目录的绝对路径
File staticDir = resource.getFile();
//staticDir.getAbsolutePath():获取 staticDir 的 绝对路径字符串。
String absolutePath = staticDir.getAbsolutePath();
//使用 Paths.get() 方法将目录路径和文件名拼接成完整的文件路径
Path path = Paths.get(absolutePath, fileName);
// Files.copy():将上传文件的输入流复制到目标路径 path 中。如果目标路径不存在,Files.copy() 会自动创建文件
Files.copy(file.getInputStream(), path);
// 生成文件访问链接
String fileUrl = String.valueOf(path);
Script script = new Script();
script.setFileUpload(fileUrl);
// 将文件链接存入数据库
return R.Success("文件链接",script);
} catch (IOException e) {
return R.Failed("文件保存失败: " + e.getMessage());
}
}
Spring ResponseEntity 类—文件下载
在 Spring Boot 中,使用 Resource
和 HttpHeaders
实现文件下载的核心原理是通过 HTTP 响应将文件内容以流的形式返回给客户端,并设置相应的 HTTP 头信息,指示浏览器将响应内容作为文件下载。
表示整个 HTTP 响应,包括状态码,标头和正文。我们可以使用它来完全配置 HTTP 响应。如果我们想使用它,我们必须从端点返回它;Spring 会处理其余的部分。
ResponseEntity 扩展了 HttpEntity 类,新增了 status 成员变量,这样,一个 ResponseEntity 基本可以代表完整的 HTTP 的请求或响应了。
Spring ResponseEntity 是一个泛型类型。因此,我们可以使用任何类型作为响应正文
核心组件
-
Resource
: Spring 提供的Resource
接口用于表示文件资源。UrlResource
是Resource
的一个实现类,用于表示通过 URL 访问的资源(如文件系统中的文件)。 -
HttpHeaders
: 用于设置 HTTP 响应头信息。在文件下载中,通常需要设置Content-Disposition
头,指示浏览器将响应内容作为附件下载。 -
ResponseEntity
: 用于封装 HTTP 响应的实体,包括状态码、响应头和响应体。
示例代码
private static final String fileDirectory = "D:\\Users\\username\\files";// 本地文件存储目录
private static final String fixedFileName = "码表模板.xlsx";// 固定文件名
@GetMapping("/download")
@ApiOperation(value = "模板下载")
public ResponseEntity<Resource> download() {
try {
// 1. 尝试从本地文件系统加载文件
Path filePath = Paths.get(fileDirectory).resolve(fixedFileName).normalize();
//将文件路径转换为 URI,然后通过 UrlResource 创建文件资源对象
Resource resource = new UrlResource(filePath.toUri());
//ClassPathResource 是 Spring 框架中的一个类,用于表示类路径(classpath)上的资源。
// 它实现了 Spring 的 Resource 接口,提供了一种方便的方式来访问类路径中的文件或资源(如配置文件、静态资源等)。
//类路径资源通常位于 src/main/resources 目录下(在 Maven/Gradle 项目中),或者被打包到 JAR 文件的根目录中。
// 2. 如果本地文件不存在,尝试从类路径(src/main/resources/static)加载文件
if (!resource.exists()) {
resource = new ClassPathResource("static/" + fixedFileName);
}
// 3. 检查文件是否存在
if (resource.exists()) {
return ResponseEntity.ok()
.header(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=\"" + resource.getFilename() + "\"")
.body(resource);
} else {
return ResponseEntity.notFound().build();
}
} catch (MalformedURLException e) {
//如果文件路径不合法(如格式错误),捕获异常并返回 500 错误
return ResponseEntity.internalServerError().build();
}
}
@GetMapping("/downloadFileName")
@ApiOperation(value = "模板下载—名字")
public ResponseEntity<Resource> downloadFileName(@RequestParam(value = "fileName") String fileName) {
try {
// 1. 尝试从本地文件系统加载文件
//Paths.get(FILE_DIRECTORY):获取文件存储目录的路径。
//resolve(fileName):将文件名与目录路径拼接,得到文件的完整路径。
//normalize():规范化路径,处理路径中的 . 和 ..
Path filePath = Paths.get(fileDirectory).resolve(fileName).normalize();
//将文件路径转换为 URI,然后通过 UrlResource 创建文件资源对象
Resource resource = new UrlResource(filePath.toUri());
// 2. 如果本地文件不存在,尝试从类路径(src/main/resources/static)加载文件
if (!resource.exists()) {
resource = new ClassPathResource("static/" + fileName);
}
if (resource.exists()) {
//HttpHeaders.CONTENT_DISPOSITION:设置响应头 Content-Disposition,指示浏览器将响应内容作为附件下载。
//attachment:表示文件应作为附件下载。
//filename:指定下载文件的默认名称。
//body(resource):将文件资源作为响应体返回。
return ResponseEntity.ok()
.header(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=\"" + resource.getFilename() + "\"")
.body(resource);
} else {
return ResponseEntity.notFound().build();
}
} catch (MalformedURLException e) {
//如果文件路径不合法(如格式错误),捕获异常并返回 500 错误
return ResponseEntity.internalServerError().build();
}
}