You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何通过Spring Boot REST API提供存储在S3上的PDF文件?

我看你现在的代码在处理S3上的PDF文件时踩了个常见的坑——用字符流(Reader)来读取二进制的PDF文件,这会直接破坏文件的字节结构,导致返回的PDF无法正常打开。咱们来修正这个问题,给出正确的Spring Boot REST API实现方式:

先说说原代码的核心问题

PDF是二进制文件,而BufferedReader、char[]这类工具是专门用来处理文本的。用字符流读取二进制流时,会因为编码转换丢失关键字节,直接导致文件损坏,这是绝对不能犯的错误。正确的做法是直接用字节流读取S3对象内容,再通过Spring Boot正确返回二进制响应。


方案一:使用InputStreamResource返回PDF(适合中小型文件)

这种方式会把文件流封装成Spring的资源对象,框架会帮我们处理流的关闭和响应输出:

import org.springframework.core.io.InputStreamResource;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import software.amazon.awssdk.services.s3.S3Client;
import software.amazon.awssdk.services.s3.model.GetObjectRequest;
import software.amazon.awssdk.services.s3.model.S3Object;

import java.io.InputStream;

@RestController
public class PdfController {

    private final S3Client s3Client;

    // 构造注入S3Client(Spring Boot推荐的依赖注入方式)
    public PdfController(S3Client s3Client) {
        this.s3Client = s3Client;
    }

    @GetMapping("/get-pdf")
    public ResponseEntity<InputStreamResource> fetchPdfFromS3() {
        String bucketName = "S3_BUCKET_NAME";
        String pdfObjectKey = "prefixUrl"; // 替换成你的PDF在S3中的实际key

        // 获取S3对象的字节流
        S3Object s3Object = s3Client.getObject(GetObjectRequest.builder()
                .bucket(bucketName)
                .key(pdfObjectKey)
                .build());
        InputStream pdfInputStream = s3Object.response().inputStream();
        InputStreamResource resource = new InputStreamResource(pdfInputStream);

        // 设置响应头,告诉浏览器这是PDF文件
        HttpHeaders headers = new HttpHeaders();
        headers.setContentType(MediaType.APPLICATION_PDF);
        // 可选:控制浏览器行为——attachment是下载,inline是在线预览
        headers.setContentDispositionFormData("attachment", "sample.pdf");

        return ResponseEntity.status(HttpStatus.OK)
                .headers(headers)
                .body(resource);
    }
}

方案二:使用StreamingResponseBody(适合大文件,避免内存溢出)

如果你的PDF文件很大,不想把整个文件加载到内存里,可以用流式输出的方式,边读边写:

import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.servlet.mvc.method.annotation.StreamingResponseBody;
import software.amazon.awssdk.services.s3.S3Client;
import software.amazon.awssdk.services.s3.model.GetObjectRequest;
import software.amazon.awssdk.services.s3.model.S3Object;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;

@RestController
public class PdfController {

    private final S3Client s3Client;

    public PdfController(S3Client s3Client) {
        this.s3Client = s3Client;
    }

    @GetMapping("/stream-pdf")
    public ResponseEntity<StreamingResponseBody> streamPdfFromS3() {
        String bucketName = "S3_BUCKET_NAME";
        String pdfObjectKey = "prefixUrl";

        S3Object s3Object = s3Client.getObject(GetObjectRequest.builder()
                .bucket(bucketName)
                .key(pdfObjectKey)
                .build());
        InputStream pdfInputStream = s3Object.response().inputStream();

        // 定义流式输出逻辑
        StreamingResponseBody responseBody = outputStream -> {
            byte[] buffer = new byte[8192]; // 8KB缓冲区
            int bytesRead;
            while ((bytesRead = pdfInputStream.read(buffer)) != -1) {
                outputStream.write(buffer, 0, bytesRead);
            }
            pdfInputStream.close(); // 手动关闭输入流,也可以用try-with-resources
        };

        HttpHeaders headers = new HttpHeaders();
        headers.setContentType(MediaType.APPLICATION_PDF);
        headers.setContentDispositionFormData("inline", "large-sample.pdf");

        return ResponseEntity.status(HttpStatus.OK)
                .headers(headers)
                .body(responseBody);
    }
}

关键注意事项

  • 永远用字节流处理二进制文件:别再用Reader、StringBuilder这类文本工具处理PDF、图片这类二进制资源了。
  • 资源安全关闭:可以用try-with-resources包裹输入流,确保即使发生异常也能正确关闭资源,避免连接泄漏。
  • S3Client配置:确保你的Spring Boot项目已经正确配置了S3Client(比如通过AWS SDK v2的starter,配置access key、secret key、region等)。
  • 响应头要准确:Content-Type必须设为application/pdf,否则浏览器可能无法正确识别文件类型;Content-Disposition可以控制文件是下载还是在线预览。

内容的提问来源于stack exchange,提问作者Shivkumar Mallesappa

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.26 08:46:38