SpringBoot校验用户上传的图片文件
- IT业界
- 2025-08-13 04:06:02

图片上传是现代应用中非常常见的一种功能,也是风险比较高的一个地方。恶意用户可能会上传一些病毒、木马。这些东西不仅严重威胁服务器的安全还浪费了带宽,磁盘等资源。所以,在图片上传的接口中,一定要对用户上传的文件进行严格的校验。
本文介绍了 2 种对图片文件进行验证的方法可供你参考。
一、文件后缀校验通过文件后缀(也就是文件扩展名,通常用于表示文件的类型),进行文件类型校验这是最常见的做法。
图片文件的后缀类型有很多,常见的只有:jpg、jpeg、gif、png、webp。我们可以在配置或者代码中定义一个“允许上传的图片后缀”集合,用于校验用户上传的图片文件。
package cn.springdoc.demo.web.controller; import java.io.IOException; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; import java.util.Set; import org.springframework.http.MediaType; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; import org.springframework.web.multipart.MultipartFile; @RestController @RequestMapping("/upload") public class UploadController { // 允许上传的图片类型的后缀集合 static final Set<String> imageSuffix = Set.of("jpg", "jpeg", "gif", "png", "webp"); @PostMapping(consumes = MediaType.MULTIPART_FORM_DATA_VALUE) public ResponseEntity<String> upload (@RequestParam("file") MultipartFile file ) throws IllegalStateException, IOException{ // 文件的原始名称 String fileName = file.getOriginalFilename(); if (fileName == null) { return ResponseEntity.badRequest().body("文件名称不能为空"); } // 解析出文件后缀 int index = fileName.lastIndexOf("."); if (index == -1) { return ResponseEntity.badRequest().body("文件后缀不能为空"); } String suffix = fileName.substring(index + 1); if (!imageSuffix.contains(suffix.trim().toLowerCase())) { return ResponseEntity.badRequest().body("非法的文件类型"); } // IO 到程序运行目录下的 public 目录,这是默认的静态资源目录 Path dir = Paths.get(System.getProperty("user.dir"), "public"); if (!Files.isDirectory(dir)) { // 创建目录 Files.createDirectories(dir); } file.transferTo(dir.resolve(fileName)); // 返回相对访问路径 return ResponseEntity.ok("/" + fileName); } }如上,代码很简单。先是获取客户端文件的名称,再从名称获取到文件的后缀。确定是合法文件后再 IO 到本地磁盘。
二、使用 ImageIO 校验由于文件的后缀是可编辑的,恶意用户可以把一个 exe 文件的后缀改为 jpg 再上传到服务器。于是这种情况下,上面的这种校验方式就会失效,恶意文件会被 IO 到磁盘。
在基于上面的方法进行校验后,我们可以先把文件 IO 到临时目录,再使用 ImageIO 类去加载图片文件,如果 Images 加载的文件不是图片,则会返回 null。
package cn.springdoc.demo.web.controller; import java.awt.image.BufferedImage; import java.io.IOException; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; import java.util.Set; import java.util.UUID; import javax.imageio.ImageIO; import org.springframework.http.MediaType; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; import org.springframework.web.multipart.MultipartFile; @RestController @RequestMapping("/upload") public class UploadController { // 允许上传的图片类型的后缀集合 static final Set<String> imageSuffix = Set.of("jpg", "jpeg", "gif", "png", "webp"); @PostMapping(consumes = MediaType.MULTIPART_FORM_DATA_VALUE) public ResponseEntity<String> upload(@RequestParam("file") MultipartFile file) throws IllegalStateException, IOException { // 文件的原始名称 String fileName = file.getOriginalFilename(); if (fileName == null) { return ResponseEntity.badRequest().body("文件名称不能为空"); } // 解析出文件后缀 int index = fileName.lastIndexOf("."); if (index == -1) { return ResponseEntity.badRequest().body("文件后缀不能为空"); } String suffix = fileName.substring(index + 1); if (!imageSuffix.contains(suffix.trim().toLowerCase())) { return ResponseEntity.badRequest().body("非法的文件类型"); } // 获取系统中的临时目录 Path tempDir = Paths.get(System.getProperty("java.io.tmpdir")); // 临时文件使用 UUID 随机命名 Path tempFile = tempDir.resolve(Paths.get(UUID.randomUUID().toString())); // copy 到临时文件 file.transferTo(tempFile); try { // 使用 ImageIO 读取文件 if (ImageIO.read(tempFile.toFile()) == null) { return ResponseEntity.badRequest().body("非法的文件类型"); } // 至此,这的确是一个图片资源文件 // IO 到运行目录下的 public 目录 Path dir = Paths.get(System.getProperty("user.dir"), "public"); if (!Files.isDirectory(dir)) { // 创建目录 Files.createDirectories(dir); } Files.copy(tempFile, dir.resolve(fileName)); // 返回相对访问路径 return ResponseEntity.ok("/" + fileName); } finally { // 始终删除临时文件 Files.delete(tempFile); } } }这种方式更为严格,不但要校验文件后缀还要校验文件内容。弊端也显而易见,会耗费更多的资源!
1、ImageIO.read 方法最后说一下 ImageIO.read 方法,它会从系统中已注册的 ImageReader 中选择一个 Reader 对图片进行解码,如果没有 Reader 能够解码文件,则返回 null。也就是说,如果上传的图片类型,在系统中没有对应的 ImageReader 也会被当做是“非法文件”。
例如:webp 类型的图片文件,ImageIO.read 就读取不了,因为 JDK 没有预置读取 webp 图片的 ImageReader。
要解决这个问题,可以添加一个 webp-imageio 依赖。
<!-- mvnrepository /artifact/org.sejda.imageio/webp-imageio --> <dependency> <groupId>org.sejda.imageio</groupId> <artifactId>webp-imageio</artifactId> <version>0.1.6</version> </dependency>webp-imageio 库提供了 webp 图片的 ImageReader 实现,并以 SPI 的形式注册到了系统中,不要写其他任何代码就可以成功读取。
SpringBoot校验用户上传的图片文件由讯客互联IT业界栏目发布,感谢您对讯客互联的认可,以及对我们原创作品以及文章的青睐,非常欢迎各位朋友分享到个人网站或者朋友圈,但转载请说明文章出处“SpringBoot校验用户上传的图片文件”
上一篇
ip数据包