Java文档在线预览实现
近期因需要完成对word、excel、ppt、txt等文档的内容检索,在用户检索到相关内容时,需要给用户提供一个在线预览文档的功能。在网上找到部分参考后,实现了该功能。
主要步骤
要实现这些文档的预览,需要先将文档转换为PDF再进行预览。
转换步骤:
* 使用OpenOffice/Aspose 将ppt、word、excel、txt类型的文件转换为pdf
预览步骤:
* 高版本浏览器上,使用pdf.js直接预览PDF文件
* 低版本浏览器上,使用swftools将PDF文件转换为swf文件,再使用flexpaper预览swf
组件安装
Aspose
由于OpenOffice的转换效果并不太佳,这里选择了Aspose
在Aspose官网下载Aspose的java版本,主要选择
* Aspose.words
* Aspose.cells(Excel)
* Aspose.slides(PPT)
* Aspose.pdf
下载完成后,在工程中引用jar包即可。
swftools
swftools主要用于将PDF文件转换为swf文件以便使用flexpaper进行播放。
在swftools下载页面 选择对应的版本下载即可。如windows下载exe后缀的文件,linux下载tar.gz后缀的文件。
flexpaper
flexpaper的作用是播放swf文件。
flexpaper官网为 https://flowpaper.com
功能实现
这里采用的所有组件版本为:
名称 | 版本 |
---|---|
Aspose.words | 16.8.0 |
Aspose.cells | 9.0.0 |
Aspose.slides | 116.7.0 |
Aspose.pdf | 11.8.0 |
swftools | swftools-2013-04-09-1007.exe |
flexpaper | 2.3.6 |
文档转换为PDF
使用Aspose进行文档转换很简单,直接引入相应的jar包,调用save方法,转换为PDF即可。
注意:
1. 使用Aspose时,每一个模块(words,cells)都可能有相同的类,如License类,SaveOptions类,SaveFormat类。而在各自模块使用时,一定要用对应模块的类
,这个坑我已爬过。
- 使用Aspose时,需要每次进行转换操作前调用设置License方法。
获取license示例代码:
package com.dm.docpreview.convert.util;
import org.apache.log4j.Logger;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.InputStream;
/**
* Aspose注册工具
*
* @author zxb
* @version 1.0.0
* 2016年10月17日 17:00
* @since Jdk1.6
*/
public class AsposeLicenseUtil {
private static InputStream inputStream = null;
private static Logger logger = Logger.getLogger(AsposeLicenseUtil.class);
/**
* 获取License的输入流
*
* @return
*/
private static InputStream getLicenseInput() {
if (inputStream == null) {
ClassLoader contextClassLoader = Thread.currentThread().getContextClassLoader();
try {
inputStream = new FileInputStream(contextClassLoader.getResource("license.xml").getPath());
} catch (FileNotFoundException e) {
logger.error("license not found!", e);
}
}
return inputStream;
}
/**
* 设置License
*
* @return true表示已成功设置License, false表示失败
*/
public static boolean setWordsLicense() {
InputStream licenseInput = getLicenseInput();
if (licenseInput != null) {
try {
com.aspose.words.License aposeLic = new com.aspose.words.License();
aposeLic.setLicense(licenseInput);
return aposeLic.getIsLicensed();
} catch (Exception e) {
logger.error("set words license error!", e);
}
}
return false;
}
/**
* 设置License
*
* @return true表示已成功设置License, false表示失败
*/
public static boolean setCellsLicense() {
InputStream licenseInput = getLicenseInput();
if (licenseInput != null) {
try {
com.aspose.cells.License aposeLic = new com.aspose.cells.License();
aposeLic.setLicense(licenseInput);
return true;
} catch (Exception e) {
logger.error("set cells license error!", e);
}
}
return false;
}
/**
* 设置License
*
* @return true表示已成功设置License, false表示失败
*/
public static boolean setSlidesLicense() {
InputStream licenseInput = getLicenseInput();
if (licenseInput != null) {
try {
com.aspose.slides.License aposeLic = new com.aspose.slides.License();
aposeLic.setLicense(licenseInput);
return aposeLic.isLicensed();
} catch (Exception e) {
logger.error("set ppt license error!", e);
}
}
return false;
}
/**
* 设置Aspose PDF的license
* @return true表示设置成功,false表示设置失败
*/
public static boolean setPdfLicense() {
InputStream licenseInput = getLicenseInput();
if (licenseInput != null) {
try {
com.aspose.pdf.License aposeLic = new com.aspose.pdf.License();
aposeLic.setLicense(licenseInput);
return true;
} catch (Exception e) {
logger.error("set pdf license error!", e);
}
}
return false;
}
}
doc转pdf示例代码,其中加水印功能可用:
package com.dm.docpreview.convert.service.impl;
import com.aspose.words.*;
import com.aspose.words.Shape;
import com.dm.docpreview.convert.domain.ConvertStatus;
import com.dm.docpreview.convert.service.File2PdfService;
import com.dm.docpreview.convert.util.AsposeLicenseUtil;
import org.apache.log4j.Logger;
import org.springframework.stereotype.Service;
import java.awt.*;
import java.io.InputStream;
import java.io.OutputStream;
/**
* 将doc文档转换为pdf文件
*
* @author zxb
* @version 1.0.0
* 2016年10月17日 16:12
* @since Jdk1.6
*/
@Service
public class Doc2PdfServiceImpl implements File2PdfService {
private Logger logger = Logger.getLogger(getClass());
@Override
public ConvertStatus convert2Pdf(InputStream inputStream, OutputStream outputStream) {
try {
if (AsposeLicenseUtil.setWordsLicense()) {
long start = System.currentTimeMillis();
Document doc = new Document(inputStream);
// insertWatermarkText(doc, "测试水印"); // 添加水印
PdfSaveOptions pdfSaveOptions = new PdfSaveOptions();
pdfSaveOptions.setSaveFormat(SaveFormat.PDF);
pdfSaveOptions.getOutlineOptions().setHeadingsOutlineLevels(3); // 设置3级doc书签需要保存到pdf的heading中
pdfSaveOptions.getOutlineOptions().setExpandedOutlineLevels(1); // 设置pdf中默认展开1级
doc.save(outputStream, pdfSaveOptions);
long end = System.currentTimeMillis();
logger.debug("convert doc2pdf completed, elapsed " + (end - start) / 1000.0 + " seconds!");
return ConvertStatus.SUCCESS;
} else {
return ConvertStatus.LICENSE_ERROR;
}
} catch (Exception e) {
logger.error("convert doc2pdf error!", e);
return ConvertStatus.CONVERT_DOC2PDF_ERROR;
}
}
/**
* Inserts a watermark into a document.
*
* @param doc The input document.
* @param watermarkText Text of the watermark.
*/
private void insertWatermarkText(Document doc, String watermarkText) throws Exception {
// Create a watermark shape. This will be a WordArt shape.
// You are free to try other shape types as watermarks.
Shape watermark = new Shape(doc, ShapeType.TEXT_PLAIN_TEXT);
// Set up the text of the watermark.
// watermark.getTextPath().setSize(16.0);
// watermark.getTextPath().setFontFamily("Arial"); // 使用Arial时最后那个字会丢
watermark.getTextPath().setFontFamily("宋体");
watermark.getTextPath().setItalic(true);
watermark.getTextPath().setText(watermarkText);
// Font size does not have effect if you specify height of the shape.
// So you can just specify height instead of specifying font size.
double fontSize = 100.0;
watermark.setWidth(watermarkText.length() * fontSize);
watermark.setHeight(fontSize);
// Text will be directed from the bottom-left to the top-right corner.
watermark.setRotation(-30);
// Remove the following two lines if you need a solid black text.
watermark.getFill().setColor(Color.lightGray); // Try LightGray to get more Word-style watermark
watermark.setStrokeColor(Color.lightGray); // Try LightGray to get more Word-style watermark
// Place the watermark in the page center.
watermark.setRelativeHorizontalPosition(RelativeHorizontalPosition.PAGE);
watermark.setRelativeVerticalPosition(RelativeVerticalPosition.PAGE);
watermark.setWrapType(WrapType.NONE);
watermark.setVerticalAlignment(VerticalAlignment.CENTER);
watermark.setHorizontalAlignment(HorizontalAlignment.CENTER);
// watermark.setHorizontalAlignment(HorizontalAlignment.LEFT);
// Create a new paragraph and append the watermark to this paragraph.
Paragraph watermarkPara = new Paragraph(doc);
watermarkPara.appendChild(watermark);
// Insert the watermark into all headers of each document section.
for (Section sect : doc.getSections()) {
// There could be up to three different headers in each section, since we want
// the watermark to appear on all pages, insert into all headers.
insertWatermarkIntoHeader(watermarkPara, sect, HeaderFooterType.HEADER_PRIMARY);
insertWatermarkIntoHeader(watermarkPara, sect, HeaderFooterType.HEADER_FIRST);
insertWatermarkIntoHeader(watermarkPara, sect, HeaderFooterType.HEADER_EVEN);
}
}
private void insertWatermarkIntoHeader(Paragraph watermarkPara, Section sect, int headerType) throws Exception {
HeaderFooter header = sect.getHeadersFooters().getByHeaderFooterType(headerType);
if (header == null) {
// There is no header of the specified type in the current section, create it.
header = new HeaderFooter(sect.getDocument(), headerType);
sect.getHeadersFooters().add(header);
}
// Insert a clone of the watermark into the header.
header.appendChild(watermarkPara.deepClone(true));
}
}
其余示例代码,在文章最末尾处将给出下载链接。
pdf.js预览
当文档被转换为PDF文件后,就可以使用pdf.js进行预览了。
在官网下载pre-built版的pdf.js,解压出来。
由于我们要集成到自己的工程中来,所以此处直接copy了viewer.html页面,修改为了我们工程需要的view_pdfjs.vm
view_pdfjs.vm
<!DOCTYPE html>
<!--
Copyright 2012 Mozilla Foundation
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
Adobe CMap resources are covered by their own copyright but the same license:
Copyright 1990-2015 Adobe Systems Incorporated.
See https://github.com/adobe-type-tools/cmap-resources
-->
<html dir="ltr" mozdisallowselectionprint moznomarginboxes>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1">
<meta name="google" content="notranslate">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<title>PDF.js viewer</title>
<link rel="stylesheet" href="#springUrl('')/res/js/pdfjs-dist/web/viewer.css">
<script src="#springUrl('')/res/js/pdfjs-dist/web/compatibility.js"></script>
<!-- This snippet is used in production (included from viewer.html) -->
<link rel="resource" type="application/l10n" href="#springUrl('')/res/js/pdfjs-dist/web/locale/locale.properties">
<script src="#springUrl('')/res/js/pdfjs-dist/web/l10n.js"></script>
<script src="#springUrl('')/res/js/pdfjs-dist/build/pdf.js"></script>
<script src="#springUrl('')/res/js/pdfjs-dist/web/debugger.js"></script>
<script src="#springUrl('')/res/js/pdfjs-dist/web/viewer.js"></script>
<script type="application/javascript">
PDFJS.workerSrc = "#springUrl('')/res/js/pdfjs-dist/build/pdf.worker.js";
/*
PDFJS.onerror = function(message, moreInfo){
var queryString = document.location.search.substring(1);
var params = parseQueryStringRegexImpl(queryString);
var file = 'file' in params ? params.file : null;
// redirect to file
if(file){
location.href = file;
}
}
function parseQueryStringRegexImpl(query){
var reg = /([^\?\=\&]+)\=([^\&]*)/g;
var obj = {};
while (reg.exec (query)) {
obj[RegExp.$1] = RegExp.$2;
}
return obj;
}
*/
</script>
</head>
<body tabindex="1" class="loadingInProgress">
<div id="outerContainer">
<div id="sidebarContainer">
<div id="toolbarSidebar">
<div class="splitToolbarButton toggled">
<button id="viewThumbnail" class="toolbarButton group toggled" title="Sh