1.场景
最近一直在跟一个项目,遇到了一个之前没做过的需求:运营在后台上传pdf文件,在官网的前台展示图片文件。之所以展示图片文件是因为在官网前台展示的是一个弹窗,要直接弹出pdf会比弹出图片的难度大一些。
2.解决方案
在搜罗了一些解决方案之后,最终敲定是使用“pdfbox”,当然相对应的还有一些其他的解决方案,大家可以酌情参考。pdfbox是比较适合我们所需的业务场景的,下面我来介绍一下实现步骤:
3.实现步骤
1.依赖引入
compile ('org.apache.pdfbox:pdfbox:2.0.9')
2.实现原理
- 获取到相应的PDF对象,对PDF按页进行遍历
- 对遍历得到的每一页PDF进行图片转换
- 把生成的图片存储到相应的文件夹中
- 加入相应的重试策略
3.实现代码
Pdf2PictureUtils:
public class Pdf2PictureUtils {
private Pdf2PictureUtils() {
}
//图片最大 临时写死 后续从redis+db取
private static final int MAXSIZE = 600;
private static final int SIZE = 10;
private static int MAX_DPI = 80;
/**
* pdfFile和targetPath 不能在同一路径下,否则不能生成图片
*
* @param pdfFile
* @param targetPath
* @return com.huajin.guofubank.business.po.PdfPicInfo
* @author ss.xin
* @date 2021/7/12 16:26
*/
public static PdfPicInfo cutPDFToJPGByPdfBox(String pdfFile, String targetPath) {
PdfPicInfo result = new PdfPicInfo();
File pdf = new File(pdfFile);
if (!pdf.exists() || !pdf.isFile()) {
return result;
}
File target = new File(targetPath);
boolean generated = target.exists() && target.isDirectory() && target.listFiles().length > 0;
target.mkdirs();
try (PDDocument doc = PDDocument.load(pdf)) {
PDFRenderer renderer = new PDFRenderer(doc);
for (int i = 0; i < doc.getNumberOfPages(); i++) {
//递归 直到符合要求
BufferedImage imageOrign = imageToBytes("PNG", renderer, i, MAX_DPI);
result.setHeight(imageOrign.getHeight());
result.setWidth(imageOrign.getWidth());
//如果文件只生成部分,mq重试,后续的也要生成!,失败时的最后一张图片可能有问题,要重新生成
//若原本需要生成五张图片,实际生成了3张,那就需要把第3,4,5张重新生成
if (!generated || i > target.listFiles().length - 2) {
File file = new File(targetPath + pdf.getName().substring(0, pdf.getName().lastIndexOf('.')) + "_" + i + ".png");
if (file.exists()) {
file.delete();
}
ImageIO.write(imageOrign, "PNG", file);
}
}
result.setCount(target.listFiles().length);
} catch (IOException e) {
e.printStackTrace();
}
return result;
}
/**
* 计算图片大小
* @param format
* @param renderer
* @param i
* @param maxDpi
* @return java.awt.image.BufferedImage
* @author ss.xin
* @date 2021/7/12 16:27
*/
public static BufferedImage imageToBytes(String format, PDFRenderer renderer, int i, int maxDpi) throws IOException {
BufferedImage imageOrign = renderer.renderImageWithDPI(i, maxDpi);
ByteArrayOutputStream out = new ByteArrayOutputStream();
try {
ImageIO.write(imageOrign, format, out);
} catch (IOException e) {
return imageOrign;
} finally {
if (out != null) {
out.close();
}
}
byte[] byteArry = out.toByteArray();
if (byteArry != null && byteArry.length / 1024 < MAXSIZE) {
return imageOrign;
}
maxDpi -= SIZE;
return imageToBytes(format, renderer, i, maxDpi);
}
}
PdfPicInfo:
@Data
@FieldDefaults(level = AccessLevel.PRIVATE)
public class PdfPicInfo {
int count;
int width;
int height;
}
4.注意点
代码中有相应的重试策略,大家可以着重理解一下其中相应的逻辑
有疑问的话,欢迎及时交流~