Spring Boot应用实现图片资源服务

在这篇文章中,我们将介绍如何使用Spring Boot创建一个REST API来提供服务器上的静态图片资源。该API包括路径安全检查、文件存在性验证以及缓存控制等功能,并且代码包含详细的注释以帮助理解。

Maven依赖

首先,在您的pom.xml文件中添加以下依赖项:

xml 复制代码
<dependencies>
    <!-- Spring Boot Starter Web for building web applications -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>

    <!-- Lombok to reduce boilerplate code -->
    <dependency>
        <groupId>org.projectlombok</groupId>
        <artifactId>lombok</artifactId>
        <scope>provided</scope>
    </dependency>

    <!-- For logging -->
    <dependency>
        <groupId>org.slf4j</groupId>
        <artifactId>slf4j-api</artifactId>
    </dependency>
</dependencies>

确保您已经正确配置了Maven项目的其他部分,如<parent>标签和版本管理等。

Java代码

接下来是核心Java代码,位于package pub.qingyun.web;包下:

java 复制代码
package pub.qingyun.web;

import lombok.extern.slf4j.Slf4j;
import org.springframework.core.io.UrlResource;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.CrossOrigin;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

import java.net.MalformedURLException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;

@RestController
@CrossOrigin
@Slf4j
public class ImageController {

    // 图片根目录
    private static final Path IMAGE_ROOT = Paths.get("F:", "Temp").toAbsolutePath().normalize();

    /**
     * 获取图片资源并返回给客户端
     *
     * @param imagePath 图片文件的相对路径参数
     * @return ResponseEntity<UrlResource> 包含图片资源的响应实体,包含适当的HTTP状态码和响应头
     */
    @GetMapping("/showImage")
    public ResponseEntity<UrlResource> getImage(@RequestParam String imagePath) {
        long start = System.currentTimeMillis();

        // 参数校验:检查图片路径是否为空
        if (imagePath == null || imagePath.trim().isEmpty()) {
            log.warn("Missing imagePath parameter");
            return ResponseEntity.badRequest().build();
        }

        // 路径安全检查:防止路径遍历攻击
        // 过滤非法字符,防止路径穿越
        if (imagePath.contains("..") || imagePath.contains("\\") || imagePath.startsWith("/")) {
            log.warn("Forbidden path access attempt: [PATH_REDACTED]");
            return ResponseEntity.status(403).build();
        }

        Path resolvedPath = IMAGE_ROOT.resolve(imagePath).normalize();
        if (!resolvedPath.startsWith(IMAGE_ROOT)) {
            log.warn("Forbidden path access attempt: [PATH_REDACTED]");
            return ResponseEntity.status(403).build();
        }

        // 文件存在性检查:验证文件是否存在且为常规文件
        if (!Files.exists(resolvedPath) || !Files.isRegularFile(resolvedPath)) {
            log.info("Image not found: [PATH_REDACTED]");
            return ResponseEntity.notFound().build();
        }

        // 缓存文件长度,避免重复调用
        long fileLength = resolvedPath.toFile().length();

        // 创建资源对象
        UrlResource resource;
        try {
            resource = new UrlResource(resolvedPath.toUri());
        } catch (MalformedURLException e) {
            log.error("Failed to create resource for: [PATH_REDACTED], Malformed URL", e);
            return ResponseEntity.status(500).build();
        } catch (Exception e) {
            log.error("Failed to create resource for: [PATH_REDACTED]", e);
            return ResponseEntity.status(500).build();
        }

        // 设置响应头信息
        HttpHeaders headers = new HttpHeaders();
        headers.setCacheControl("public, max-age=86400"); // 缓存 1 天
        headers.setExpires(System.currentTimeMillis() + 86400_000L); // 过期时间
        headers.setContentDispositionFormData("inline", resource.getFilename());

        log.info("getImage [PATH_REDACTED] ({} bytes) cost: {}ms", fileLength, (System.currentTimeMillis() - start));

        // 构建并返回成功响应
        return ResponseEntity.ok()
                .headers(headers)
                .contentType(MediaType.IMAGE_JPEG)
                .contentLength(fileLength)
                .body(resource);
    }
}

这个代码段展示了一个简单的Spring Boot REST控制器,用于从指定路径加载图片并将其作为资源返回给客户端。它包括必要的安全措施来防止潜在的路径遍历攻击,并设置了缓存控制头以提高性能。希望这段代码能为您提供构建自己的图片服务的帮助!