You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

如何在Web应用响应中返回图片转PDF生成的文件?

解决图片转PDF后返回给客户端的问题

你现在的核心问题是Controller没返回任何内容,而且Service把PDF写到了服务器本地文件里,客户端根本拿不到。这里给你两种实用方案,按需挑选:

方案一:直接返回PDF字节流(推荐,无需服务器存储)

这种方式不用把PDF存到服务器磁盘,直接在内存中生成后返回给客户端,更高效也不用处理文件清理的麻烦。

1. 修改Service代码,返回PDF字节数组

把原来写本地文件的逻辑改成用ByteArrayOutputStream,这样能直接拿到生成的PDF字节数据:

public byte[] convert(MultipartFile[] files) throws IOException {
    List<ImageData> imagesData = Arrays.stream(files)
            .map(file -> {
                try {
                    return ImageDataFactory.create(file.getBytes());
                } catch (IOException e) {
                    throw new RuntimeException("读取图片文件失败", e);
                }
            })
            .collect(Collectors.toList());

    // 用try-with-resources自动关闭流,避免资源泄漏
    try (ByteArrayOutputStream baos = new ByteArrayOutputStream();
         PdfDocument pdfDocument = new PdfDocument(new PdfWriter(baos));
         Document document = new Document(pdfDocument)) {

        for (ImageData image : imagesData) {
            Image img = new Image(image);
            img.setWidth(pdfDocument.getDefaultPageSize().getWidth() - 50);
            img.setAutoScaleHeight(true);
            document.add(img);
            // 优化:最后一张图片后不要加空白页
            if (!imagesData.get(imagesData.size() - 1).equals(image)) {
                pdfDocument.addNewPage();
            }
        }
        return baos.toByteArray();
    }
}

2. 修改Controller代码,返回PDF响应

把Controller的返回类型改成ResponseEntity<byte[]>,设置正确的响应头让浏览器直接触发下载:

@PostMapping(path = "/upload", consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
public ResponseEntity<byte[]> uploadFiles(@RequestParam("files") MultipartFile[] files) throws IOException {
    // 先校验文件格式,只允许JPEG/PNG
    for (MultipartFile file : files) {
        String contentType = file.getContentType();
        if (!"image/jpeg".equals(contentType) && !"image/png".equals(contentType)) {
            throw new IllegalArgumentException("仅支持JPEG和PNG格式的图片");
        }
    }

    byte[] pdfBytes = imageToPdfConversionService.convert(files);

    return ResponseEntity.ok()
            .contentType(MediaType.APPLICATION_PDF)
            .header(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=\"converted_images.pdf\"")
            .body(pdfBytes);
}

这样客户端上传图片后,浏览器会直接弹出下载窗口,文件名是converted_images.pdf

方案二:存储PDF到服务器,返回下载链接

如果需要保留生成的PDF(比如用户后续可能再次下载),可以先把文件存到服务器临时目录,再返回下载URL,前端显示下载按钮触发下载。

1. 修改Service,返回存储的文件路径

public String convert(MultipartFile[] files) throws IOException {
    // 用系统临时目录,避免硬编码路径
    String tempDir = System.getProperty("java.io.tmpdir");
    // 加时间戳避免文件名重复
    String pdfFileName = "converted_" + System.currentTimeMillis() + ".pdf";
    String pdfFilePath = tempDir + File.separator + pdfFileName;

    List<ImageData> imagesData = Arrays.stream(files)
            .map(file -> {
                try {
                    return ImageDataFactory.create(file.getBytes());
                } catch (IOException e) {
                    throw new RuntimeException("读取图片文件失败", e);
                }
            })
            .collect(Collectors.toList());

    try (PdfDocument pdfDocument = new PdfDocument(new PdfWriter(pdfFilePath));
         Document document = new Document(pdfDocument)) {

        for (ImageData image : imagesData) {
            Image img = new Image(image);
            img.setWidth(pdfDocument.getDefaultPageSize().getWidth() - 50);
            img.setAutoScaleHeight(true);
            document.add(img);
            if (!imagesData.get(imagesData.size() - 1).equals(image)) {
                pdfDocument.addNewPage();
            }
        }
    }

    // 返回下载接口的URL
    return "/download/pdf?fileName=" + pdfFileName;
}

2. 添加下载Controller接口

@GetMapping("/download/pdf")
public ResponseEntity<Resource> downloadPdf(@RequestParam("fileName") String fileName) {
    String tempDir = System.getProperty("java.io.tmpdir");
    File pdfFile = new File(tempDir + File.separator + fileName);

    if (!pdfFile.exists()) {
        return ResponseEntity.notFound().build();
    }

    UrlResource resource;
    try {
        resource = new UrlResource(pdfFile.toURI());
    } catch (MalformedURLException e) {
        return ResponseEntity.internalServerError().build();
    }

    return ResponseEntity.ok()
            .contentType(MediaType.APPLICATION_PDF)
            .header(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=\"" + pdfFile.getName() + "\"")
            .body(resource);
}

3. 前端处理

前端在上传成功后,会收到类似/download/pdf?fileName=converted_1234567890.pdf的URL,你可以创建一个“下载PDF”按钮,点击时跳转到这个URL,就能触发浏览器下载。

⚠️ 注意:用这种方案一定要定期清理服务器上的临时PDF文件,避免磁盘空间被占满——可以加定时任务,或者在用户下载完成后删除文件。

额外建议

  • 异常处理:可以全局捕获RuntimeExceptionIOException,返回更友好的错误提示给客户端。
  • 文件大小限制:在配置里设置MultipartFile的最大上传大小,避免超大文件导致内存溢出。

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

火山引擎 最新活动