Java实现文档视频缩略图:Spire插件实战

1434 字
7 分钟
Java实现文档视频缩略图:Spire插件实战

📝 本文基于作者实际开发经验,已在项目中成功使用 Spire 插件生成多种格式的缩略图。

背景#

很多业务系统需要展示文档和视频的缩略图——比如文件管理系统的列表预览、审批流程中的附件概览等。Java 生态里处理这个问题主要有两条路:

  • Apache POI + PDFBox:开源方案,但 Word/PPT 的渲染效果一般,样式兼容性差
  • Spire 系列:商业组件,API 简洁,渲染效果好,支持 doc/docx/ppt/pptx/pdf

本文使用 Spire 系列(spire.docspire.presentationspire.pdf)来实现 Word、PPT、PDF 的缩略图生成,视频则使用 JavaCV(FFmpeg)截取第一帧。

依赖配置#

Spire 的免费版(Free Spire.Doc/Office/PDF)有页数限制,正式使用建议用商业版。

<!-- Spire.Doc -->
<dependency>
<groupId>e-iceblue</groupId>
<artifactId>spire.doc.free</artifactId>
<version>5.4.2</version>
</dependency>
<!-- Spire.Presentation -->
<dependency>
<groupId>e-iceblue</groupId>
<artifactId>spire.presentation.free</artifactId>
<version>5.4.0</version>
</dependency>
<!-- Spire.Pdf -->
<dependency>
<groupId>e-iceblue</groupId>
<artifactId>spire.pdf.free</artifactId>
<version>8.6.0</version>
</dependency>
<!-- JavaCV(视频截帧) -->
<dependency>
<groupId>org.bytedeco</groupId>
<artifactId>javacv-platform</artifactId>
<version>1.5.9</version>
</dependency>

注意:Spire 的 jar 包可能无法通过 Maven 公共仓库直接下载,需要手动配置 e-iceblue 的私有仓库,或直接将 jar 包安装到本地 Maven 仓库。

1. Word 缩略图#

使用 Document.saveToImages() 将第一页渲染为图片,直接写入 PNG 文件。

private static String wordToImage(String wordFile, String outputImgPath) throws Exception {
Document word = new Document();
word.loadFromFile(wordFile);
String fileName = getFileName(wordFile);
BufferedImage image = word.saveToImages(0, ImageType.Bitmap);
String imgUrl = outputImgPath + fileName + (0 + 1) + ".png";
File file = new File(imgUrl);
ImageIO.write(image, "PNG", file);
log.info("word缩略图获取完成,图片目录:[{}]", imgUrl);
return imgUrl;
}

2. PPT 缩略图#

PPT 取第一页幻灯片,用 saveAsImage() 渲染。注意用完后要调用 dispose() 释放资源。

private static String pptToImage(String pptFile, String outputImgPath) throws Exception {
Presentation ppt = new Presentation();
ppt.loadFromFile(pptFile);
String fileName = getFileName(pptFile);
BufferedImage image = ppt.getSlides().get(0).saveAsImage();
String imgUrl = outputImgPath + fileName + (0 + 1) + ".png";
ImageIO.write(image, "PNG", new File(imgUrl));
ppt.dispose();
log.info("ppt缩略图获取完成,图片目录:[{}]", imgUrl);
return imgUrl;
}

3. PDF 缩略图#

PDF 同样取第一页,saveAsImage() 的第二个参数指定输出图片类型。

private static String pdfToImage(String pdfFile, String outputImgPath) throws Exception {
PdfDocument pdf = new PdfDocument();
pdf.loadFromFile(pdfFile);
String fileName = getFileName(pdfFile);
BufferedImage image = pdf.saveAsImage(0, PdfImageType.Bitmap);
String imgUrl = outputImgPath + fileName + (0 + 1) + ".png";
File file = new File(imgUrl);
ImageIO.write(image, "PNG", file);
log.info("pdf缩略图获取完成,图片目录:[{}]", imgUrl);
return imgUrl;
}

4. 视频缩略图#

视频使用 JavaCV 的 FFmpegFrameGrabber 截取第一帧。如果第一帧为空(某些视频编码问题),会返回空字符串。

private static String videoImage(String videoFile, String outputImgPath) throws FrameGrabber.Exception {
FFmpegFrameGrabber ff = FFmpegFrameGrabber.createDefault(videoFile);
ff.start();
Frame f = ff.grabImage();
String pngPath = getFileName(videoFile) + ".png";
String resPath = outputImgPath + pngPath;
String imageMat = "PNG";
if (null == f || null == f.image) {
return "";
}
Java2DFrameConverter converter = new Java2DFrameConverter();
BufferedImage bufferedImage = converter.getBufferedImage(f);
File output = new File(resPath);
try {
ImageIO.write(bufferedImage, imageMat, output);
} catch (IOException e) {
e.printStackTrace();
}
log.info("video缩略图获取完成,图片目录:[{}]", resPath);
ff.stop();
return resPath;
}

5. 通用入口方法#

根据文件后缀自动路由到对应的处理方法:

public static String generateThumbnail(String file, String outputImgPath) throws Exception {
if (!outputImgPath.endsWith(File.separator)) {
outputImgPath += File.separator;
}
String filePath = "";
if (file.endsWith("doc") | file.endsWith("docx")) {
filePath = wordToImage(file, outputImgPath);
} else if (file.endsWith("ppt") | file.endsWith("pptx")) {
filePath = pptToImage(file, outputImgPath);
} else if (file.endsWith("pdf")) {
filePath = pdfToImage(file, outputImgPath);
} else if (file.endsWith("mp4")) {
filePath = videoImage(file, outputImgPath);
}
return filePath;
}

文件名使用时间戳 + 原始文件名的方式避免冲突:

private static String getFileName(String filePath) {
String fileName = new File(filePath).getName();
return System.currentTimeMillis() + "-" + fileName.substring(0, fileName.lastIndexOf("."));
}

6. Linux 部署踩坑:中文字体缺失#

项目部署到 Linux 后,生成的缩略图里中文字符全部显示为方框或空白。原因是 Linux 默认没有安装中文字体,Spire 渲染时找不到对应的字形。

解决方案:安装中文字体。

Terminal window
# 安装 fontconfig 和 mkfontscale
yum install -y fontconfig
yum install -y mkfontscale
# 创建字体目录
mkdir -p /usr/share/fonts/chinese/
chmod -R 775 /usr/share/fonts/chinese/
# 将 Windows 字体(C:\Windows\Fonts)打包上传到服务器,解压到上面的目录
# 然后执行以下命令刷新字体缓存
cd /usr/share/fonts
mkfontscale
mkfontdir
fc-cache -fv
# 验证中文字体是否生效
fc-list :lang=zh

安装完成后重启应用,缩略图中的中文就能正常显示了。

完整工具类#

import com.spire.doc.Document;
import com.spire.doc.documents.ImageType;
import com.spire.pdf.PdfDocument;
import com.spire.pdf.graphics.PdfImageType;
import com.spire.presentation.Presentation;
import lombok.extern.slf4j.Slf4j;
import org.bytedeco.javacv.FFmpegFrameGrabber;
import org.bytedeco.javacv.Frame;
import org.bytedeco.javacv.FrameGrabber;
import org.bytedeco.javacv.Java2DFrameConverter;
import javax.imageio.ImageIO;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
@Slf4j
public class ThumbnailUtil {
private static String getFileName(String filePath) {
String fileName = new File(filePath).getName();
return System.currentTimeMillis() + "-" + fileName.substring(0, fileName.lastIndexOf("."));
}
private static String wordToImage(String wordFile, String outputImgPath) throws Exception {
Document word = new Document();
word.loadFromFile(wordFile);
String fileName = getFileName(wordFile);
BufferedImage image = word.saveToImages(0, ImageType.Bitmap);
String imgUrl = outputImgPath + fileName + (0 + 1) + ".png";
File file = new File(imgUrl);
ImageIO.write(image, "PNG", file);
log.info("word缩略图获取完成,图片目录:[{}]", imgUrl);
return imgUrl;
}
private static String pptToImage(String pptFile, String outputImgPath) throws Exception {
Presentation ppt = new Presentation();
ppt.loadFromFile(pptFile);
String fileName = getFileName(pptFile);
BufferedImage image = ppt.getSlides().get(0).saveAsImage();
String imgUrl = outputImgPath + fileName + (0 + 1) + ".png";
ImageIO.write(image, "PNG", new File(imgUrl));
ppt.dispose();
log.info("ppt缩略图获取完成,图片目录:[{}]", imgUrl);
return imgUrl;
}
private static String pdfToImage(String pdfFile, String outputImgPath) throws Exception {
PdfDocument pdf = new PdfDocument();
pdf.loadFromFile(pdfFile);
String fileName = getFileName(pdfFile);
BufferedImage image = pdf.saveAsImage(0, PdfImageType.Bitmap);
String imgUrl = outputImgPath + fileName + (0 + 1) + ".png";
File file = new File(imgUrl);
ImageIO.write(image, "PNG", file);
log.info("pdf缩略图获取完成,图片目录:[{}]", imgUrl);
return imgUrl;
}
private static String videoImage(String videoFile, String outputImgPath) throws FrameGrabber.Exception {
FFmpegFrameGrabber ff = FFmpegFrameGrabber.createDefault(videoFile);
ff.start();
Frame f = ff.grabImage();
String pngPath = getFileName(videoFile) + ".png";
String resPath = outputImgPath + pngPath;
if (null == f || null == f.image) {
return "";
}
Java2DFrameConverter converter = new Java2DFrameConverter();
BufferedImage bufferedImage = converter.getBufferedImage(f);
File output = new File(resPath);
try {
ImageIO.write(bufferedImage, "PNG", output);
} catch (IOException e) {
e.printStackTrace();
}
log.info("video缩略图获取完成,图片目录:[{}]", resPath);
ff.stop();
return resPath;
}
public static String generateThumbnail(String file, String outputImgPath) throws Exception {
if (!outputImgPath.endsWith(File.separator)) {
outputImgPath += File.separator;
}
String filePath = "";
if (file.endsWith("doc") | file.endsWith("docx")) {
filePath = wordToImage(file, outputImgPath);
} else if (file.endsWith("ppt") | file.endsWith("pptx")) {
filePath = pptToImage(file, outputImgPath);
} else if (file.endsWith("pdf")) {
filePath = pdfToImage(file, outputImgPath);
} else if (file.endsWith("mp4")) {
filePath = videoImage(file, outputImgPath);
}
return filePath;
}
public static void main(String[] args) throws Exception {
String path = generateThumbnail("D:\\desktop\\test\\video.mp4",
"D:\\desktop\\test\\img");
System.out.println(path);
}
}

相关推荐#

文章分享

如果这篇文章对你有帮助,欢迎分享给更多人!

Java实现文档视频缩略图:Spire插件实战
https://blog.wmovie.site/posts/java-document-thumbnails/
作者
朵朵
发布于
2023-01-15
许可协议
CC BY-NC-SA 4.0
Profile Image of the Author
朵朵
全栈开发者,5 年 Java/SpringBoot + Vue/React 开发经验,曾维护 Delphi 遗留系统。业余深耕 NAS 与软路由,专注 Homelab 家庭网络搭建及 Docker 自托管应用实战。坐标福州,正迈向独立开发之路。
公告
欢迎来到我的博客!有什么想要的邮箱告诉我,最近沉迷nas和软路由。
分类
标签
站点统计
文章
25
分类
4
标签
18
总字数
32,908
运行时长
0
最后活动
0 天前

文章目录