Java 实现图片转 PDF(单张 多张合并)
·
Java 实现图片转 PDF(单张 / 多张合并)
在日常开发中,图片转 PDF 是非常常见的需求,比如证件照生成 PDF、多图片合并成一个 PDF 文件等。本文基于实际项目代码,详细讲解如何用 Java 实现单张图片转 PDF、多张图片合并为单个 PDF,同时支持本地图片和网络图片(URL),并附上完整的代码、依赖配置和注意事项。
一、技术选型与环境说明
1. 核心依赖
本文实现图片转 PDF 的核心依赖是 Aspose.PDF for Java(处理 PDF 文档),辅助依赖包括 Hutool(文件操作)、Apache Commons Codec(Base64 编码)、PDFBox(IO 工具)等。
2. 环境要求
- JDK 版本:JDK 8 及以上(兼容 JDK 11/17)
- 构建工具:Maven/Gradle(本文以 Maven 为例)
二、Maven 依赖配置
在pom.xml中引入以下依赖,覆盖核心功能所需的所有组件:
<!-- Aspose.PDF 核心依赖(图片转PDF核心) -->
<dependency>
<groupId>com.aspose</groupId>
<artifactId>aspose-pdf</artifactId>
<version>24.1</version>
</dependency>
<!-- Hutool 工具类(文件操作简化) -->
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-all</artifactId>
<version>5.8.22</version>
</dependency>
<!-- Apache Commons Codec(Base64编码) -->
<dependency>
<groupId>commons-codec</groupId>
<artifactId>commons-codec</artifactId>
<version>1.15</version>
</dependency>
<!-- Apache PDFBox(IO流工具) -->
<dependency>
<groupId>org.apache.pdfbox</groupId>
<artifactId>pdfbox</artifactId>
<version>2.0.32</version>
</dependency>
<!-- 日志依赖(可选,与项目日志框架匹配) -->
<dependency>
<groupId>commons-logging</groupId>
<artifactId>commons-logging</artifactId>
<version>1.2</version>
</dependency>
三、核心功能实现
1. 工具类完整代码
封装PdfUtils工具类,包含单张图片转 PDF、多张图片合并 PDF、URL 图片转 PDF、PDF 转 Base64 等核心功能:
package org.dromara.common.utils.file.pdf;
import cn.hutool.core.io.FileUtil;
import com.aspose.pdf.Document;
import com.aspose.pdf.Image;
import com.aspose.pdf.Page;
import com.aspose.pdf.Rectangle;
import org.apache.commons.codec.binary.Base64;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.pdfbox.io.IOUtils;
import java.io.*;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.List;
/**
* PDF转化工具类
* 支持:单张图片转PDF、多张图片合并PDF、URL图片转PDF、PDF文件转Base64
* JDK版本:JDK 8+
* 核心依赖:Aspose.PDF for Java
*/
public class PdfUtils {
private static final Log log = LogFactory.getLog(PdfUtils.class);
/**
* 单个本地图片转PDF
* @param file 本地图片文件
* @return 生成的PDF文件(临时文件)
*/
public static File imgToPdfFile(File file) {
long startTime = System.currentTimeMillis();
log.info("------------开始执行图片转PDF------------");
FileInputStream imageStream = null;
Document doc = null;
try {
String pdfName = file.getName();
// 创建临时PDF文件(前缀img_,后缀pdf)
File pdfPath = File.createTempFile("img_", "pdf");
// 拼接最终PDF路径(临时文件+原图片名)
String pdfFilePath = pdfPath + pdfName;
// 1. 初始化PDF文档
doc = new Document();
// 2. 添加PDF页面并设置尺寸(A4:595x842)
Page page = doc.getPages().add();
page.setCropBox(new Rectangle(0, 0, 595, 842));
// 3. 读取图片流并添加到PDF页面
imageStream = new FileInputStream(file.getPath());
Image image1 = new Image();
page.getParagraphs().add(image1);
image1.setImageStream(imageStream);
// 4. 保存PDF文件
doc.save(pdfFilePath);
// 校验文件是否存在
File mOutputPdfFile = new File(pdfFilePath);
if (!mOutputPdfFile.exists()) {
mOutputPdfFile.deleteOnExit();
return null;
}
log.info("------------图片转PDF执行完成-----------耗时:" + (System.currentTimeMillis() - startTime));
return mOutputPdfFile;
} catch (IOException e) {
log.error("图片转PDF失败:file:" + file.getPath() + ", error:" + e.getMessage());
} finally {
// 关闭流和文档,避免资源泄漏
if (imageStream != null) {
try {
imageStream.close();
} catch (IOException e) {
throw new RuntimeException(e);
}
}
if (doc != null) {
doc.close();
}
}
return null;
}
/**
* 网络图片(URL)转PDF
* @param url 图片URL地址(http/https)
* @return 生成的PDF文件
*/
public static File imgToPdfFile(URL url) {
try (InputStream inputStream = url.openStream();) {
// 将网络图片保存为临时文件,再调用本地图片转PDF方法
File tempFile = FileUtil.createTempFile();
FileUtil.writeFromStream(inputStream, tempFile);
return PdfUtils.imgToPdfFile(tempFile);
} catch (Exception e) {
throw new RuntimeException("网络图片转PDF失败:" + e.getMessage(), e);
}
}
/**
* 网络图片(URL字符串)转PDF
* @param urlPath 图片URL字符串
* @return 生成的PDF文件
*/
public static File imgToPdfFile(String urlPath) {
try {
URL url = new URL(urlPath);
// 校验URL协议(仅支持http/https)
if (!url.getProtocol().equals("http") && !url.getProtocol().equals("https")) {
throw new RuntimeException("url地址必须是http/https类型地址");
}
return imgToPdfFile(url);
} catch (MalformedURLException e) {
throw new RuntimeException("URL格式错误:" + urlPath, e);
}
}
/**
* PDF文件转Base64编码
* @param file PDF文件
* @return Base64字符串
*/
public final static String encodeBase64String(File file) {
try {
return Base64.encodeBase64String(IOUtils.toByteArray(new FileInputStream(file)));
} catch (IOException e) {
throw new RuntimeException("PDF文件转Base64失败:" + e.getMessage(), e);
}
}
/**
* 多张本地图片合并为单个PDF(核心私有方法)
* @param imageFiles 本地图片文件数组
* @return 合并后的PDF文件
*/
private static File mergeImagesToPdf(File[] imageFiles) {
// 入参校验
if (imageFiles == null || imageFiles.length == 0) {
log.error("图片文件列表不能为空");
return null;
}
long startTime = System.currentTimeMillis();
log.info("------------开始执行多张图片转PDF------------,共" + imageFiles.length + "张图片");
FileInputStream imageStream = null;
Document doc = null;
File pdfFile = null;
try {
// 创建合并后的临时PDF文件
pdfFile = File.createTempFile("merged_img_", ".pdf");
pdfFile.deleteOnExit(); // JVM退出时自动删除临时文件
String pdfFilePath = pdfFile.getAbsolutePath();
// 初始化PDF文档
doc = new Document();
// 遍历图片,每张图片生成一个PDF页面
for (File imageFile : imageFiles) {
if (imageFile == null || !imageFile.exists()) {
log.warn("图片文件无效,跳过:" + (imageFile == null ? "null" : imageFile.getPath()));
continue;
}
// 添加新页面并设置尺寸
Page page = doc.getPages().add();
page.setCropBox(new Rectangle(0, 0, 595, 842));
// 读取图片并添加到页面
imageStream = new FileInputStream(imageFile);
Image image = new Image();
page.getParagraphs().add(image);
image.setImageStream(imageStream);
// 关闭当前图片流,避免资源泄漏
if (imageStream != null) {
imageStream.close();
imageStream = null;
}
}
// 保存合并后的PDF
if (doc.getPages().size() > 0) {
doc.save(pdfFilePath);
} else {
// 无有效页面,删除空文件
if (pdfFile.exists()) {
pdfFile.delete();
}
return null;
}
log.info("------------多张图片转PDF-----------耗时:" + (System.currentTimeMillis() - startTime));
return pdfFile;
} catch (IOException e) {
// 异常时删除无效PDF文件
if (pdfFile != null && pdfFile.exists()) {
pdfFile.delete();
}
log.error("多张图片合并PDF失败:" + e.getMessage(), e);
return null;
} finally {
// 关闭所有资源
try {
if (imageStream != null) {
imageStream.close();
}
} catch (IOException e) {
log.error("关闭图片流失败", e);
}
try {
if (doc != null) {
doc.close();
}
} catch (Exception e) {
log.error("关闭PDF文档失败", e);
}
}
}
/**
* 多张图片转PDF(支持合并/分开生成)
* @param imageFiles 图片文件数组
* @param isOverride true=合并为一个PDF,false=每张图片生成一个PDF
* @return PDF文件数组
*/
public static File[] mergeImagesToPdf(File[] imageFiles, Boolean isOverride) {
if (isOverride) {
// 合并为单个PDF
File mergedPdf = mergeImagesToPdf(imageFiles);
return mergedPdf != null ? new File[]{mergedPdf} : new File[0];
}
// 每张图片生成一个PDF
File[] pdfFiles = new File[imageFiles.length];
for (int i = 0; i < imageFiles.length; i++) {
pdfFiles[i] = imgToPdfFile(imageFiles[i]);
}
return pdfFiles;
}
/**
* 多张网络图片(URL字符串)转PDF
* @param imageFiles 图片URL列表
* @param isOverride true=合并为一个PDF,false=每张图片生成一个PDF
* @return PDF文件数组
*/
public static File[] mergeImagesToPdf(List<String> imageFiles, Boolean isOverride) {
try {
File[] files = new File[imageFiles.size()];
for (int i = 0; i < imageFiles.size(); i++) {
URL url = new URL(imageFiles.get(i));
// 校验URL协议
if (!url.getProtocol().equals("http") && !url.getProtocol().equals("https")) {
throw new RuntimeException("url地址必须是http/https类型地址:" + imageFiles.get(i));
}
// 下载网络图片到临时文件
try (InputStream inputStream = url.openStream();) {
File tempFile = FileUtil.createTempFile();
FileUtil.writeFromStream(inputStream, tempFile);
files[i] = tempFile;
} catch (Exception e) {
throw new RuntimeException("下载图片失败:" + imageFiles.get(i), e);
}
}
return mergeImagesToPdf(files, isOverride);
} catch (MalformedURLException e) {
throw new RuntimeException("URL格式错误", e);
}
}
/**
* 多张网络图片合并为单个PDF
* @param imageFiles 图片URL列表
* @return 合并后的PDF文件
*/
public static File[] mergeImagesToPdf(List<String> imageFiles, Boolean isOverride) {
//转化为File文件
File tempFile = null;
try {
File[] files = new File[imageFiles.size()];
for (int i = 0; i < imageFiles.size(); i++) {
URL url = null;
url = new URL(imageFiles.get(i));
if (!url.getProtocol().equals("http") && !url.getProtocol().equals("https")) {
throw new RuntimeException("url地址必须是http类型地址");
}
try (InputStream inputStream = url.openStream();) {
tempFile = FileUtil.createTempFile();
FileUtil.writeFromStream(inputStream, tempFile);
files[i] = tempFile;
} catch (Exception e) {
throw new RuntimeException(e);
}
}
return mergeImagesToPdf(files, isOverride);
} catch (MalformedURLException e) {
throw new RuntimeException(e);
} finally {
if (tempFile.exists()) {
tempFile.delete();
}
}
}
2. 关键功能说明
| 方法 | 功能 | 核心逻辑 |
|---|---|---|
imgToPdfFile(File file) |
本地图片转 PDF | 创建临时 PDF 文件 → 初始化 PDF 文档 → 添加图片到 PDF 页面 → 保存文件 |
imgToPdfFile(String urlPath) |
URL 图片转 PDF | 解析 URL → 下载图片到临时文件 → 调用本地图片转 PDF 方法 |
mergeImagesToPdf(File[] imageFiles) |
多张图片合并 PDF | 遍历图片 → 为每张图片创建 PDF 页面 → 合并保存为单个 PDF |
encodeBase64String(File file) |
PDF 转 Base64 | 读取 PDF 文件字节流 → 进行 Base64 编码 |
四、使用示例
1. 单张本地图片转 PDF
public static void main(String[] args) {
// 单张本地图片转PDF
File imgFile = new File("D:/test/photo.jpg");
File pdfFile = PdfUtils.imgToPdfFile(imgFile);
System.out.println("生成的PDF路径:" + pdfFile.getAbsolutePath());
// PDF转Base64
String base64 = PdfUtils.encodeBase64String(pdfFile);
System.out.println("PDF Base64编码:" + base64);
}
2. 多张网络图片合并为单个 PDF
public static void main(String[] args) {
// 多张网络图片URL
List<String> imgUrls = new ArrayList<>();
imgUrls.add("https://example.com/img1.jpg");
imgUrls.add("https://example.com/img2.png");
// 合并为单个PDF
File mergedPdf = PdfUtils.mergeImagesToPdf(imgUrls);
System.out.println("合并后的PDF路径:" + mergedPdf.getAbsolutePath());
}
3. 多张图片(部分合并、部分分开)
public static void main(String[] args) {
// 本地图片数组
File[] imgFiles = new File[]{
new File("D:/test/1.jpg"),
new File("D:/test/2.png")
};
// false=每张图片生成一个PDF
File[] pdfFiles = PdfUtils.mergeImagesToPdf(imgFiles, false);
for (File pdf : pdfFiles) {
System.out.println("生成的PDF路径:" + pdf.getAbsolutePath());
}
}
五、注意事项
- 资源关闭:图片流、PDF 文档必须在 finally 块中关闭,避免内存泄漏和文件句柄占用。
- Aspose.PDF 授权:Aspose.PDF 免费版会有水印,生产环境需购买授权或替换为开源方案(如 iText、PDFBox)。
- URL 校验:网络图片 URL 需校验协议(仅支持 http/https),避免非法 URL 导致异常。
- 异常处理:代码中对 IO 异常、URL 格式异常做了基础处理,可根据业务需求扩展(如重试机制、告警通知)。
六、总结
本文基于 Aspose.PDF 实现了一套完整的图片转 PDF 解决方案,支持本地 / 网络图片、单张 / 多张合并,兼容 JDK 8 及以上版本。核心亮点:
- 封装性强:所有功能集中在
PdfUtils工具类,调用简单; - 资源安全:完善的流和文档关闭逻辑,避免资源泄漏;
- 灵活性高:支持合并 / 分开生成 PDF,满足不同业务场景;
- 兼容性好:支持 http/https 协议的网络图片,适配常见图片格式。
更多推荐




所有评论(0)