kkFileView优化PDF图片预览增加JPEG2000标准图片支持
2023-06-13 09:16:44 时间
kkFileView 预览特殊 PDF 文件时白屏问题
# 前言
项目在使用 kkFileView 时接到反馈说部分 PDF 在预览时没有内容,显示空白图片。
查看官方issue
也发现很多类似问题,但是也没有详尽好用的解决办法。
仅仅修改依赖增加特殊标准图片的处理会降低页面打开速度,因为图片转换操作比较耗时,所以我们还需要把图片转换操作改成异步并使用多线程去增加转换效率。
下面介绍一下如何更好地处理这个 PDF 特殊图片解析问题。
# 项目修改
- 依赖添加。
PDF 以图片模式预览时无内容是因为包含 JPEG2000 标准的图片,而 kk 并没有添加此类图片的解析依赖,所以我们要在
pom.xml
添加相关依赖,这个与网上搜到的方法大致相同。
<dependency>
<groupId>com.github.jai-imageio</groupId>
<artifactId>jai-imageio-core</artifactId>
<version>1.3.1</version>
</dependency>
<dependency>
<groupId>org.apache.pdfbox</groupId>
<artifactId>jbig2-imageio</artifactId>
<version>3.0.2</version>
</dependency>
<dependency>
<groupId>com.github.jai-imageio</groupId>
<artifactId>jai-imageio-jpeg2000</artifactId>
<version>1.3.0</version>
</dependency>
- 图片转换代码优化。 接到转换请求之后根据线程数及 PDF 总页数分配图片转换任务给每个线程,已经转换完成的页码不会重复转换。
/**
* pdf文件转换成jpg图片集
* @param pdfFilePath pdf文件路径
* @param pdfName pdf文件名称
* @param baseUrl 基础访问地址
* @return 图片访问集合
*/
public List<String> pdf2jpg(String pdfFilePath, String pdfName, String baseUrl) {
List<String> imageUrls = new ArrayList<>();
Integer imageCount = this.getConvertedPdfImage(pdfFilePath);
String imageFileSuffix = ".jpg";
String pdfFolder = pdfName.substring(0, pdfName.length() - 4);
String urlPrefix;
try {
urlPrefix = baseUrl + URLEncoder.encode(pdfFolder, uriEncoding).replaceAll("\\+", "%20");
} catch (UnsupportedEncodingException e) {
logger.error("UnsupportedEncodingException", e);
urlPrefix = baseUrl + pdfFolder;
}
// 如果当前pdf已缓存,则直接返回
try {
PDDocument doc = PDDocument.load(new File(pdfFilePath));
PDFRenderer pdfRendererMulti = new PDFRenderer(doc);
int pageCount = doc.getNumberOfPages();
int index = pdfFilePath.lastIndexOf(".");
String folder = pdfFilePath.substring(0, index);
for (int i = 0; i < pageCount; i++) {
imageUrls.add(urlPrefix + "/" + i + imageFileSuffix);
}
Integer pdf2jpgLock = this.getConvertedPdfImage(pdfFilePath.concat("_LOCK"));
if (pdf2jpgLock != null && pdf2jpgLock > 0 || (imageCount != null && imageCount > 0)) {
return imageUrls;
}
File path = new File(folder);
if (!path.exists() && !path.mkdirs()) {
logger.error("创建转换文件【{}】目录失败,请检查目录权限!", folder);
}
CompletableFuture.runAsync(() -> {
List<CompletableFuture> futures = new ArrayList<>();
for (int i = 0; i < pageCount; i++) {
int finalI = i;
CompletableFuture<String> future = CompletableFuture.supplyAsync(() ->
this.pdf2jpg(pdfRendererMulti, pdfFilePath, pdfName, baseUrl, finalI), commonThreadPool);
futures.add(future);
}
CompletableFuture.allOf(futures.toArray(new CompletableFuture[0])).join();
this.addConvertedPdfImage(pdfFilePath, pageCount);
this.addConvertedPdfImage(pdfFilePath.concat("_LOCK"), 0);
try {
doc.close();
logger.info("doc close");
} catch (IOException e) {
logger.error("doc close error", e);
}
}, commonThreadPool);
} catch (Exception e) {
this.addConvertedPdfImage(pdfFilePath.concat("_LOCK"), 0);
logger.error("Convert pdf to jpg exception, pdfFilePath:{}", pdfFilePath, e);
}
return imageUrls;
}
/**
* pdf文件转换成jpg图片集
* @param pdfFilePath pdf文件路径
* @param pdfName pdf文件名称
* @param baseUrl 基础访问地址
* @param pageIndex 当前页
* @return 图片访问集合
*/
public String pdf2jpg(PDFRenderer pdfRendererMulti, String pdfFilePath, String pdfName, String baseUrl, int pageIndex) {
logger.info("current thread {}, currentIndex:{}", Thread.currentThread().getName(), pageIndex);
this.addConvertedPdfImage(pdfFilePath.concat("_LOCK"), pageIndex + 1);
String imageFileSuffixMulti = ".jpg";
String pdfFolder = pdfName.substring(0, pdfName.length() - 4);
String urlPrefix;
try {
urlPrefix = baseUrl + URLEncoder.encode(pdfFolder, uriEncoding).replaceAll("\\+", "%20");
} catch (UnsupportedEncodingException e) {
logger.error("UnsupportedEncodingException", e);
urlPrefix = baseUrl + pdfFolder;
}
// 判断文件是否已存在,已存在直接返回
String imageUrl = urlPrefix + "/" + pageIndex + imageFileSuffixMulti;
File path = new File(imageUrl);
if (path.exists()) {
logger.info("{} 文件已存在!", imageUrl);
return imageUrl;
}
// 图片不存在需要转换
try {
int index = pdfFilePath.lastIndexOf(".");
String folder = pdfFilePath.substring(0, index);
String imageFilePath = folder + File.separator + pageIndex + imageFileSuffixMulti;
BufferedImage imageResource = pdfRendererMulti.renderImageWithDPI(pageIndex, 105, ImageType.RGB);
ImageIOUtil.writeImage(imageResource, imageFilePath, 105);
// 释放对象
imageResource.getGraphics().dispose();
imageResource = null;
} catch (IOException e) {
this.addConvertedPdfImage(pdfFilePath.concat("_LOCK"), 0);
logger.error("Convert pdf to jpg exception, pdfFilePath:{}", pdfFilePath, e);
}
return imageUrl;
}
- 页面图片加载优化。 正在转换的图片是无法正常显示的,所以在加载出错时隔一段时间再去请求图片,直到图片转换完成可以成功显示为止。
<!-- 在图片加载标签内添加加载出错事件处理 -->
<div class="img-area">
<img
class="my-photo"
alt="loading"
onerror="imgError(this)"
data-src="${img}"
src="images/loading.gif"
/>
</div>
图片加载报错处理:
// 图片加载出错时默认显示加载动画,6秒后显示原图
function imgError(img) {
img.setAttribute("src", "images/loading.gif") let t = setTimeout(function () {
img.setAttribute("src", $(img).data('src')) clearTimeout(t) }, 6000)
}
# 建议
本文只是提供一个修改思路,在实际使用过程中会略微减慢 PDF 的预览速度(图片解析需要时间),原本正常的图片也会打开地慢一点,如果确实有相关特殊 PDF 的预览需求可以参考处理。
# 参考资料
相关文章
- 怎样免费完美的把PDF转Word?
- java导出pdf模板_java模板导出PDF[通俗易懂]
- java 打印pdf_java打印pdf文件
- stm32f4库函数开发指南 pdf_c语言常用的库函数表
- Recompress for Mac(PDF优化压缩软件)
- pdf文件用什么方式打开-电脑上的PDF怎么都变成Edge浏览器打开了?怎么更改PDF文件打开方式?
- PDF编辑软件强势来袭!Acrobat XI升级Acrobat DC安装教程pdf编辑器全版本下载
- PDF编辑软件强势来袭!Acrobat XI升级Acrobat DC安装教程pdf编辑器全版本下载
- Acrobat Pro DC 2021 for Mac(编辑pdf文件)
- 用python批量获取公募基金季报pdf
- PDF Expert for mac(pdf编辑工具) v3.0.37(910)中文激活版
- Word批量转PDF工具 便携版
- Adobe acrobat软件下载安装教程-全版本PDF编辑器-acrobat 预览
- MySQL学习指南:一步一步掌握PDF格式教程(mysql学习pdf)
- 阅读器Linux下的福昕PDF阅读技术实现(linux福昕pdf)
- Linux使用从入门到精通PDF指南(linux使用教程pdf)
- 工具Linux下的PDF格式转换神器(linuxpdf转换)
- 文件Linux参考手册:PDF文件版本(linux参考手册pdf)
- Linux源码精要:PDF详解(linux源码pdf)
- Linux系统移植:手把手带你搞定PDF(linux系统移植pdf)
- MySQL权威指南:PDF版本(mysql权威指南pdf)
- Linux命令行指南:全面解读PDF版(linux命令行大全pdf)
- 在Linux中轻松打开PDF文件(linux打开pdf)
- 在 Linux 上使用PDF图片的技巧.(pdf图片linux)
- 如何使用MySQL优化技巧提高PDF文档处理效率(mysql优化pdf)
- Linux系统下使用PDF文件的方法(linux pdf 打开)
- Redis高级教程PDF打造高效存储系统(redis高级教程pdf)
- Oracle终极手册完全掌握PDF书籍(oracle pdf书籍)