一、背景
在项目中,我们有一个接口用于将 pdf 按页拆分为图片,方便前端逐页展示。
原始实现逻辑:
- 使用 apache pdfbox 的
pdfrenderer - 调用
renderimagewithdpi - 使用
imageio.write(image, "png", file)输出 png - 每一页生成一张大图,路径存入数据库
上线一段时间后发现:
- 服务器磁盘增长很快
- 图片加载明显变慢
- 图片接口内存压力升高
- 单页图片体积动辄几 mb
于是开始排查问题。
二、问题分析
造成图片体积过大的核心原因有 3 个:
dpi 过高
原始代码:
renderer.renderimagewithdpi(i, 144);
144 dpi 会生成较高分辨率图片,页面尺寸较大。
dpi 越高,生成的图片分辨率越大,文件体积呈指数级增长。
不做尺寸限制
直接按 pdf 原始尺寸渲染输出。
例如:
- a4 页面在 144 dpi 下
- 宽度可能接近 2000+ 像素
对于仅用于网页展示来说完全没有必要。
使用 png 无损格式
png 是无损压缩,适合:
- 图标
- 透明图片
- 插画
但对于:
- 含大量文字 + 图片的课件 pdf
- 整页截图型内容
png 文件体积会非常大。
总结一句话
高 dpi + 原始大尺寸 + png 无损 = 单页图片非常大
三、优化目标
在保证肉眼清晰可读的前提下:
- 降低图片体积
- 降低磁盘增长速度
- 减少网络传输时间
- 降低内存压力
并且:
- 不改接口签名
- 不改 controller
- 只在工具类内部优化
四、整体优化方案
优化策略:
- 降低 dpi
- 限制最大宽度,等比例缩放
- 保持 png(兼容现有业务)
五、最终实现代码(核心优化版)
public class pdftoimageutil {
/**
* 将 pdf 逐页转为压缩后的 png 图片
*/
public static list<bizpptimage> convertpdftoimages(file pdffile,
string outputdir,
string coursedetailid) throws ioexception {
list<bizpptimage> list = new arraylist<>();
try (pddocument doc = pddocument.load(pdffile)) {
pdfrenderer renderer = new pdfrenderer(doc);
int pagecount = doc.getnumberofpages();
for (int i = 0; i < pagecount; i++) {
// ① 降低 dpi(原 144 → 96)
bufferedimage sourceimage = renderer.renderimagewithdpi(i, 96);
// ② 限制最大宽度,等比例缩放
bufferedimage scaledimage = scaleifnecessary(sourceimage, 1280);
string filename = uuid.randomuuid() + "_" + (i + 1) + ".png";
file outfile = new file(outputdir, filename);
imageio.write(scaledimage, "png", outfile);
bizpptimage vo = new bizpptimage();
vo.setimagepath(outfile.getabsolutepath());
vo.setfilename(filename);
vo.setcoursedetailid(coursedetailid);
list.add(vo);
}
}
return list;
}
/**
* 按最大宽度等比例缩放
*/
private static bufferedimage scaleifnecessary(bufferedimage source, int maxwidth) {
int width = source.getwidth();
int height = source.getheight();
if (width <= maxwidth) {
return source;
}
double scale = (double) maxwidth / width;
int targetwidth = maxwidth;
int targetheight = (int) math.round(height * scale);
bufferedimage target = new bufferedimage(
targetwidth,
targetheight,
bufferedimage.type_int_rgb
);
graphics2d g2d = target.creategraphics();
g2d.setrenderinghint(renderinghints.key_interpolation,
renderinghints.value_interpolation_bilinear);
g2d.setrenderinghint(renderinghints.key_rendering,
renderinghints.value_render_quality);
g2d.setrenderinghint(renderinghints.key_antialiasing,
renderinghints.value_antialias_on);
g2d.drawimage(source, 0, 0, targetwidth, targetheight, null);
g2d.dispose();
return target;
}
}
六、参数调优建议
平衡清晰度 + 体积(推荐)
- dpi = 96
- maxwidth = 1280
适合 pc 展示场景。
清晰度优先
- dpi = 120
- maxwidth = 1600 ~ 1920
适合高分屏场景。
体积优先(缩略图/预览)
- dpi = 72
- maxwidth = 800 ~ 1024
图片体积可以大幅降低。
七、优化顺序建议
建议按以下步骤逐步调优:
- 固定 dpi(例如 96)
- 调整 maxwidth
- 观察清晰度和体积
- 如仍然过大,再降低 dpi
八、如果体积仍然偏大
可以考虑:
改为 jpeg
jpeg 是有损压缩,体积会显著下降。
缺点:
- 需要自定义
imagewriter - 需要设置压缩质量参数
- 对文字边缘可能略微模糊
如果对清晰度要求不极端,可以考虑此方案。
九、优化效果验证方式
同一份 pdf:
- 用老代码生成图片
- 用新代码生成图片
对比:
ls -lh
du -sh 目录名
重点观察:
- 单页图片大小
- 总目录大小
- 前端加载速度
- 服务器磁盘增长趋势
十、最终效果
在本项目中:
原方案:
- dpi = 144
- 不缩放
- 单页 2~5mb
优化后:
- dpi = 96
- maxwidth = 1280
结果:
- 单页体积显著下降
- 总目录大小大幅缩减
- 前端加载速度明显提升
- 磁盘增长速度趋缓
并且:
- 接口签名未变
- controller 无感知
- 业务层零改动
十一、经验总结
pdf 转图片时,一定要考虑:
- dpi
- 输出尺寸
- 图片格式
默认配置往往偏“高清优先”,不适合大规模线上使用。
一句话总结:pdf 转图片不优化,迟早磁盘爆炸。
到此这篇关于java使用apache pdfbox进行pdf转图片体积过大的优化指南的文章就介绍到这了,更多相关java解决pdf转图片体积过大内容请搜索代码网以前的文章或继续浏览下面的相关文章希望大家以后多多支持代码网!
发表评论