kkFileView优化PDF图片预览增加JPEG2000标准图片支持
2023-02-25 18:21:18 时间
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 的预览需求可以参考处理。
# 参考资料
相关文章
- Jgit的使用笔记
- 利用Github Action实现Tornadofx/JavaFx打包
- 叹息!GitHub Trending 即将成为历史!
- 微软软了?开源社区讨论炸锅,GitHub CEO 亲自来答
- GitHub Trending 列表频现重复项,前后端都没去重?
- Photoshop Elements 2021版本软件安装教程(mac+windows全版本都有)
- (ps全版本)Photoshop 2020的安装与破解教程(mac+windows全版本都有)
- (ps全版本)Photoshop cc2018的安装与破解教程(mac+windows全版本,包括2023
- 环境搭建:Oracle GoldenGate 大数据迁移到 Redshift/Flat file/Flume/Kafka测试流程
- 每个开发人员都要掌握的:最小 Linux 基础课
- 来撸羊毛了!Windows 环境下 Hexo 博客搭建,并部署到 GitHub Pages
- 超实用!手把手入门 MongoDB:这些坑点请一定远离
- 【GitHub日报】22-10-09 zustand、neovim、webtorrent、express 等4款App今日上新
- 【GitHub日报】22-10-10 brew、minio、vite、seaweedfs、dbeaver 等8款App今日上新
- 【GitHub日报】22-10-11 cobra、grafana、vue、ToolJet、redwood 等13款App今日上新
- Photoshop 2018 下载及安装教程(mac+windows全版本都有,包括最新的2023)
- Photoshop 2017 下载及安装教程(mac+windows全版本都有,包括最新的2023)
- Photoshop 2020 下载及安装教程(mac+windows全版本都有,包括最新的2023)
- Photoshop 2023 资源免费下载(mac+windows全版本都有,包括最新的2023)
- 最新版本Photoshop CC2018软件安装教程(mac+windows全版本都有,包括2023