近期因需要完成對word、excel、ppt、txt等文檔的內容檢索,在用戶檢索到相關內容時,需要給用戶提供一個在線預覽文檔的功能。在網上找到部分參考后,實現了該功能。
主要步驟
要實現這些文檔的預覽,需要先將文檔轉換為PDF再進行預覽。
轉換步驟:
- 使用OpenOffice/Aspose 將ppt、word、excel、txt類型的文件轉換為pdf
預覽步驟:
組件安裝
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即可。
注意:
- 使用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="Show Thumbnails" tabindex="2" data-l10n-id="thumbs">
<span data-l10n-id="thumbs_label">Thumbnails</span>
</button>
<button id="viewOutline" class="toolbarButton group" title="Show Document Outline" tabindex="3" data-l10n-id="outline">
<span data-l10n-id="outline_label">Document Outline</span>
</button>
<button id="viewAttachments" class="toolbarButton group" title="Show Attachments" tabindex="4" data-l10n-id="attachments">
<span data-l10n-id="attachments_label">Attachments</span>
</button>
</div>
</div>
<div id="sidebarContent">
<div id="thumbnailView">
</div>
<div id="outlineView" class="hidden">
</div>
<div id="attachmentsView" class="hidden">
</div>
</div>
</div> <!-- sidebarContainer -->
<div id="mainContainer">
<div class="findbar hidden doorHanger hiddenSmallView" id="findbar">
<label for="findInput" class="toolbarLabel" data-l10n-id="find_label">Find:</label>
<input id="findInput" class="toolbarField" tabindex="91">
<div class="splitToolbarButton">
<button class="toolbarButton findPrevious" title="" id="findPrevious" tabindex="92" data-l10n-id="find_previous">
<span data-l10n-id="find_previous_label">Previous</span>
</button>
<div class="splitToolbarButtonSeparator"></div>
<button class="toolbarButton findNext" title="" id="findNext" tabindex="93" data-l10n-id="find_next">
<span data-l10n-id="find_next_label">Next</span>
</button>
</div>
<input type="checkbox" id="findHighlightAll" class="toolbarField" tabindex="94">
<label for="findHighlightAll" class="toolbarLabel" data-l10n-id="find_highlight">Highlight all</label>
<input type="checkbox" id="findMatchCase" class="toolbarField" tabindex="95">
<label for="findMatchCase" class="toolbarLabel" data-l10n-id="find_match_case_label">Match case</label>
<span id="findResultsCount" class="toolbarLabel hidden"></span>
<span id="findMsg" class="toolbarLabel"></span>
</div> <!-- findbar -->
<div id="secondaryToolbar" class="secondaryToolbar hidden doorHangerRight">
<div id="secondaryToolbarButtonContainer">
<button id="secondaryPresentationMode" class="secondaryToolbarButton presentationMode visibleLargeView" title="Switch to Presentation Mode" tabindex="51" data-l10n-id="presentation_mode">
<span data-l10n-id="presentation_mode_label">Presentation Mode</span>
</button>
<button id="secondaryOpenFile" class="secondaryToolbarButton openFile visibleLargeView" title="Open File" tabindex="52" data-l10n-id="open_file">
<span data-l10n-id="open_file_label">Open</span>
</button>
<button id="secondaryPrint" class="secondaryToolbarButton print visibleMediumView" title="Print" tabindex="53" data-l10n-id="print">
<span data-l10n-id="print_label">Print</span>
</button>
<button id="secondaryDownload" class="secondaryToolbarButton download visibleMediumView" title="Download" tabindex="54" data-l10n-id="download">
<span data-l10n-id="download_label">Download</span>
</button>
<a href="#" id="secondaryViewBookmark" class="secondaryToolbarButton bookmark visibleSmallView" title="Current view (copy or open in new window)" tabindex="55" data-l10n-id="bookmark">
<span data-l10n-id="bookmark_label">Current View</span>
</a>
<div class="horizontalToolbarSeparator visibleLargeView"></div>
<button id="firstPage" class="secondaryToolbarButton firstPage" title="Go to First Page" tabindex="56" data-l10n-id="first_page">
<span data-l10n-id="first_page_label">Go to First Page</span>
</button>
<button id="lastPage" class="secondaryToolbarButton lastPage" title="Go to Last Page" tabindex="57" data-l10n-id="last_page">
<span data-l10n-id="last_page_label">Go to Last Page</span>
</button>
<div class="horizontalToolbarSeparator"></div>
<button id="pageRotateCw" class="secondaryToolbarButton rotateCw" title="Rotate Clockwise" tabindex="58" data-l10n-id="page_rotate_cw">
<span data-l10n-id="page_rotate_cw_label">Rotate Clockwise</span>
</button>
<button id="pageRotateCcw" class="secondaryToolbarButton rotateCcw" title="Rotate Counterclockwise" tabindex="59" data-l10n-id="page_rotate_ccw">
<span data-l10n-id="page_rotate_ccw_label">Rotate Counterclockwise</span>
</button>
<div class="horizontalToolbarSeparator"></div>
<button id="toggleHandTool" class="secondaryToolbarButton handTool" title="Enable hand tool" tabindex="60" data-l10n-id="hand_tool_enable">
<span data-l10n-id="hand_tool_enable_label">Enable hand tool</span>
</button>
<div class="horizontalToolbarSeparator"></div>
<button id="documentProperties" class="secondaryToolbarButton documentProperties" title="Document Properties…" tabindex="61" data-l10n-id="document_properties">
<span data-l10n-id="document_properties_label">Document Properties…</span>
</button>
</div>
</div> <!-- secondaryToolbar -->
<div class="toolbar">
<div id="toolbarContainer">
<div id="toolbarViewer">
<div id="toolbarViewerLeft">
<button id="sidebarToggle" class="toolbarButton" title="Toggle Sidebar" tabindex="11" data-l10n-id="toggle_sidebar">
<span data-l10n-id="toggle_sidebar_label">Toggle Sidebar</span>
</button>
<div class="toolbarButtonSpacer"></div>
<button id="viewFind" class="toolbarButton group hiddenSmallView" title="Find in Document" tabindex="12" data-l10n-id="findbar">
<span data-l10n-id="findbar_label">Find</span>
</button>
<div class="splitToolbarButton">
<button class="toolbarButton pageUp" title="Previous Page" id="previous" tabindex="13" data-l10n-id="previous">
<span data-l10n-id="previous_label">Previous</span>
</button>
<div class="splitToolbarButtonSeparator"></div>
<button class="toolbarButton pageDown" title="Next Page" id="next" tabindex="14" data-l10n-id="next">
<span data-l10n-id="next_label">Next</span>
</button>
</div>
<label id="pageNumberLabel" class="toolbarLabel" for="pageNumber" data-l10n-id="page_label">Page: </label>
<input type="number" id="pageNumber" class="toolbarField pageNumber" value="1" size="4" min="1" tabindex="15">
<span id="numPages" class="toolbarLabel"></span>
</div>
<div id="toolbarViewerRight">
<button id="presentationMode" class="toolbarButton presentationMode hiddenLargeView" title="Switch to Presentation Mode" tabindex="31" data-l10n-id="presentation_mode">
<span data-l10n-id="presentation_mode_label">Presentation Mode</span>
</button>
<button id="openFile" class="toolbarButton openFile hiddenLargeView" title="Open File" tabindex="32" data-l10n-id="open_file">
<span data-l10n-id="open_file_label">Open</span>
</button>
<button id="print" class="toolbarButton print hiddenMediumView" title="Print" tabindex="33" data-l10n-id="print">
<span data-l10n-id="print_label">Print</span>
</button>
<button id="download" class="toolbarButton download hiddenMediumView" title="Download" tabindex="34" data-l10n-id="download">
<span data-l10n-id="download_label">Download</span>
</button>
<a href="#" id="viewBookmark" class="toolbarButton bookmark hiddenSmallView" title="Current view (copy or open in new window)" tabindex="35" data-l10n-id="bookmark">
<span data-l10n-id="bookmark_label">Current View</span>
</a>
<div class="verticalToolbarSeparator hiddenSmallView"></div>
<button id="secondaryToolbarToggle" class="toolbarButton" title="Tools" tabindex="36" data-l10n-id="tools">
<span data-l10n-id="tools_label">Tools</span>
</button>
</div>
<div class="outerCenter">
<div class="innerCenter" id="toolbarViewerMiddle">
<div class="splitToolbarButton">
<button id="zoomOut" class="toolbarButton zoomOut" title="Zoom Out" tabindex="21" data-l10n-id="zoom_out">
<span data-l10n-id="zoom_out_label">Zoom Out</span>
</button>
<div class="splitToolbarButtonSeparator"></div>
<button id="zoomIn" class="toolbarButton zoomIn" title="Zoom In" tabindex="22" data-l10n-id="zoom_in">
<span data-l10n-id="zoom_in_label">Zoom In</span>
</button>
</div>
<span id="scaleSelectContainer" class="dropdownToolbarButton">
<select id="scaleSelect" title="Zoom" tabindex="23" data-l10n-id="zoom">
<option id="pageAutoOption" title="" value="auto" selected="selected" data-l10n-id="page_scale_auto">Automatic Zoom</option>
<option id="pageActualOption" title="" value="page-actual" data-l10n-id="page_scale_actual">Actual Size</option>
<option id="pageFitOption" title="" value="page-fit" data-l10n-id="page_scale_fit">Fit Page</option>
<option id="pageWidthOption" title="" value="page-width" data-l10n-id="page_scale_width">Full Width</option>
<option id="customScaleOption" title="" value="custom"></option>
<option title="" value="0.5" data-l10n-id="page_scale_percent" data-l10n-args='{ "scale": 50 }'>50%</option>
<option title="" value="0.75" data-l10n-id="page_scale_percent" data-l10n-args='{ "scale": 75 }'>75%</option>
<option title="" value="1" data-l10n-id="page_scale_percent" data-l10n-args='{ "scale": 100 }'>100%</option>
<option title="" value="1.25" data-l10n-id="page_scale_percent" data-l10n-args='{ "scale": 125 }'>125%</option>
<option title="" value="1.5" data-l10n-id="page_scale_percent" data-l10n-args='{ "scale": 150 }'>150%</option>
<option title="" value="2" data-l10n-id="page_scale_percent" data-l10n-args='{ "scale": 200 }'>200%</option>
<option title="" value="3" data-l10n-id="page_scale_percent" data-l10n-args='{ "scale": 300 }'>300%</option>
<option title="" value="4" data-l10n-id="page_scale_percent" data-l10n-args='{ "scale": 400 }'>400%</option>
</select>
</span>
</div>
</div>
</div>
<div id="loadingBar">
<div class="progress">
<div class="glimmer">
</div>
</div>
</div>
</div>
</div>
<menu type="context" id="viewerContextMenu">
<menuitem id="contextFirstPage" label="First Page"
data-l10n-id="first_page"></menuitem>
<menuitem id="contextLastPage" label="Last Page"
data-l10n-id="last_page"></menuitem>
<menuitem id="contextPageRotateCw" label="Rotate Clockwise"
data-l10n-id="page_rotate_cw"></menuitem>
<menuitem id="contextPageRotateCcw" label="Rotate Counter-Clockwise"
data-l10n-id="page_rotate_ccw"></menuitem>
</menu>
<div id="viewerContainer" tabindex="0">
<div id="viewer" class="pdfViewer"></div>
</div>
<div id="errorWrapper" hidden='true'>
<div id="errorMessageLeft">
<span id="errorMessage"></span>
<button id="errorShowMore" data-l10n-id="error_more_info">
More Information
</button>
<button id="errorShowLess" data-l10n-id="error_less_info" hidden='true'>
Less Information
</button>
</div>
<div id="errorMessageRight">
<button id="errorClose" data-l10n-id="error_close">
Close
</button>
</div>
<div class="clearBoth"></div>
<textarea id="errorMoreInfo" hidden='true' readonly="readonly"></textarea>
</div>
</div> <!-- mainContainer -->
<div id="overlayContainer" class="hidden">
<div id="passwordOverlay" class="container hidden">
<div class="dialog">
<div class="row">
<p id="passwordText" data-l10n-id="password_label">Enter the password to open this PDF file:</p>
</div>
<div class="row">
<!-- The type="password" attribute is set via script, to prevent warnings in Firefox for all http:// documents. -->
<input id="password" class="toolbarField">
</div>
<div class="buttonRow">
<button id="passwordCancel" class="overlayButton"><span data-l10n-id="password_cancel">Cancel</span></button>
<button id="passwordSubmit" class="overlayButton"><span data-l10n-id="password_ok">OK</span></button>
</div>
</div>
</div>
<div id="documentPropertiesOverlay" class="container hidden">
<div class="dialog">
<div class="row">
<span data-l10n-id="document_properties_file_name">File name:</span> <p id="fileNameField">-</p>
</div>
<div class="row">
<span data-l10n-id="document_properties_file_size">File size:</span> <p id="fileSizeField">-</p>
</div>
<div class="separator"></div>
<div class="row">
<span data-l10n-id="document_properties_title">Title:</span> <p id="titleField">-</p>
</div>
<div class="row">
<span data-l10n-id="document_properties_author">Author:</span> <p id="authorField">-</p>
</div>
<div class="row">
<span data-l10n-id="document_properties_subject">Subject:</span> <p id="subjectField">-</p>
</div>
<div class="row">
<span data-l10n-id="document_properties_keywords">Keywords:</span> <p id="keywordsField">-</p>
</div>
<div class="row">
<span data-l10n-id="document_properties_creation_date">Creation Date:</span> <p id="creationDateField">-</p>
</div>
<div class="row">
<span data-l10n-id="document_properties_modification_date">Modification Date:</span> <p id="modificationDateField">-</p>
</div>
<div class="row">
<span data-l10n-id="document_properties_creator">Creator:</span> <p id="creatorField">-</p>
</div>
<div class="separator"></div>
<div class="row">
<span data-l10n-id="document_properties_producer">PDF Producer:</span> <p id="producerField">-</p>
</div>
<div class="row">
<span data-l10n-id="document_properties_version">PDF Version:</span> <p id="versionField">-</p>
</div>
<div class="row">
<span data-l10n-id="document_properties_page_count">Page Count:</span> <p id="pageCountField">-</p>
</div>
<div class="buttonRow">
<button id="documentPropertiesClose" class="overlayButton"><span data-l10n-id="document_properties_close">Close</span></button>
</div>
</div>
</div>
</div> <!-- overlayContainer -->
</div> <!-- outerContainer -->
<div id="printContainer"></div>
<div id="mozPrintCallback-shim" hidden>
<style>
@media print {
#printContainer div {
page-break-after: always;
page-break-inside: avoid;
}
}
</style>
<style scoped>
#mozPrintCallback-shim {
position: fixed;
top: 0;
left: 0;
height: 100%;
width: 100%;
z-index: 9999999;
display: block;
text-align: center;
background-color: rgba(0, 0, 0, 0.5);
}
#mozPrintCallback-shim[hidden] {
display: none;
}
@media print {
#mozPrintCallback-shim {
display: none;
}
}
#mozPrintCallback-shim .mozPrintCallback-dialog-box {
display: inline-block;
margin: -50px auto 0;
position: relative;
top: 45%;
left: 0;
min-width: 220px;
max-width: 400px;
padding: 9px;
border: 1px solid hsla(0, 0%, 0%, .5);
border-radius: 2px;
box-shadow: 0 1px 4px rgba(0, 0, 0, 0.3);
background-color: #474747;
color: hsl(0, 0%, 85%);
font-size: 16px;
line-height: 20px;
}
#mozPrintCallback-shim .progress-row {
clear: both;
padding: 1em 0;
}
#mozPrintCallback-shim progress {
width: 100%;
}
#mozPrintCallback-shim .relative-progress {
clear: both;
float: right;
}
#mozPrintCallback-shim .progress-actions {
clear: both;
}
</style>
<div class="mozPrintCallback-dialog-box">
<!-- TODO: Localise the following strings -->
Preparing document for printing...
<div class="progress-row">
<progress value="0" max="100"></progress>
<span class="relative-progress">0%</span>
</div>
<div class="progress-actions">
<input type="button" value="Cancel" class="mozPrintCallback-cancel">
</div>
</div>
</div>
</body>
</html>
其中需要注意的是pdf.worker.js
默認是在viewer.js
中指定路徑的,使用相對路徑來指定的。
function configure(PDFJS) {
PDFJS.imageResourcesPath = './images/';
//PDFJS.workerSrc = '../build/pdf.worker.js';
PDFJS.cMapUrl = '../web/cmaps/';
PDFJS.cMapPacked = true;
}
此處注釋了 PDFJS.workerSrc = '../build/pdf.worker.js';
然后在我們的view_pdfjs.vm中手動指定了該js路徑為絕對路徑:
PDFJS.workerSrc = "#springUrl('')/res/js/pdfjs-dist/build/pdf.worker.js";
預覽
pdf.js使用參數file來指定需要預覽的文件。例如我們的view_pdfjs.vm對應的controller地址為:http://localhost:8080/docpreview/docpreview/Preview_preview.do?file=abc.pdf
跨域
如果pdf.js預覽頁面同pdf文件資源在同一個域下,此處可以忽略。
pdf.js使用的異步請求來請求的pdf文件資源,這也就意味着會存在跨域問題。pdf.js中主動檢測了file參數對應的地址是否是同一個域中。
我目前采用的方式是封裝一個同域下的.do請求,該.do請求會拿到請求資源然后再回寫。實現后的地址如下:
http://localhost:8080/docpreview/docpreview/Preview_preview.do?file=http://localhost:8080/docpreview/docpreview/Preview_crossDomainSource.do?filePath=ftp://localhost/test.pdf
crossDomainSource.do代碼示例:
/**
* 將跨域的資源轉換為本地的資源
*
* @param request
* @param response
* @return
* @throws Exception
*/
public ModelAndView crossDomainSource(HttpServletRequest request, HttpServletResponse response) throws Exception {
// 如果為本域資源
String filePath = request.getParameter("filePath");
if (StringUtil.isNotEmpty(filePath)) {
try {
writeResponse(response, filePath);
} catch (FileNotFoundException e) {
logger.error("file not found!", e);
write404(request, response);
} catch (IOException e) {
logger.error("get file error!", e);
write500(request, response);
}
} else {
write404(request, response);
}
return null;
}
/**
* 取出filePath對應的數據並寫入response中
*
* @param response 響應
* @param filePath 文件路徑
* @throws IOException
*/
private void writeResponse(HttpServletResponse response, String filePath) throws IOException {
InputStream inputStream = null;
URLConnection conn = null;
try {
URL url = new URL(filePath);
conn = url.openConnection();
conn.setConnectTimeout(30000); // 30s
conn.setReadTimeout(30000);
conn.connect();
inputStream = new DataInputStream(conn.getInputStream());
copyStream(inputStream, response.getOutputStream());
} finally {
if (conn != null) {
if (conn instanceof HttpURLConnection) {
((HttpURLConnection) conn).disconnect();
}
}
}
}
實現效果
PDF轉換為swf
pdf轉swf則需要通過swftools工具來完成。由於swftools為外部命令,在java中需要通過Runtime.getRuntime().exec(commandStr);
這種方式來調用。
swftools轉換pdf為swf文件命令示例:
pdf2swf.exe test.pdf -o "test.swf" -z -s flashversion=9 -s languagedir="xpdf-chinese-simplified" -s storeallcharacters -f -j 100
其中languagedir需要下載xpdf的簡體中文包即可。其它參數請自行查看官方文檔。
swftools中的java代碼示例:
package com.dm.docpreview.convert.service.impl;
import com.dm.docpreview.convert.domain.ConvertStatus;
import com.dm.docpreview.convert.domain.SwfConfig;
import org.apache.commons.io.IOUtils;
import org.junit.Test;
import java.io.*;
import static junit.framework.Assert.assertTrue;
/**
* @author zxb
* @version 1.0.0
* 2016年10月18日 17:15
* @since Jdk1.6
*/
public class Pdf2SwfServiceImplTest {
@Test
public void pdf2swf() throws Exception {
// 配置輸入pdf及輸出的swf
String inputFilePath = getClass().getClassLoader().getResource("convertFile/test.pdf").getPath();
File inputFile = new File(inputFilePath);
File outputFile = new File(inputFile.getParentFile().getParent() + File.separator + "outputFile" + File.separator + "test.swf");
InputStream inputStream = new FileInputStream(inputFile);
OutputStream outputStream = new FileOutputStream(outputFile);
// 配置SwfTools參數
SwfConfig swfConfig = new SwfConfig();
swfConfig.setFilePath("C:\\zxbProgramFiles\\workdir\\java\\swftools\\pdf2swf.exe");
swfConfig.setLanguageDir("C:\\zxbProgramFiles\\workdir\\java\\swftools\\xpdf-chinese-simplified");
swfConfig.setQuality(100);
// 轉換pdf為swf
Pdf2SwfServiceImpl pdf2SwfService = new Pdf2SwfServiceImpl();
pdf2SwfService.setSwfConfig(swfConfig);
ConvertStatus convertStatus = pdf2SwfService.pdf2swf(inputStream, outputStream);
// 關閉流
IOUtils.closeQuietly(outputStream);
IOUtils.closeQuietly(inputStream);
assertTrue(convertStatus == ConvertStatus.SUCCESS);
}
}
flexpaper預覽
將下載好的flexpaper安裝包解壓。
把其中的js、FlexPaperViewer.swf、css、index.html
等拷貝到工程目錄中。
其中index.html已經有一個flexpaper的使用示例了,這里直接將index.html的內容copy到我們的 view_flexpaper.vm文件中。
view_flexpaper.vm文件內容:
<!doctype html>
<html>
<head>
<title>FlexPaper</title>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<meta name="viewport" content="initial-scale=1,user-scalable=no,maximum-scale=1,width=device-width" />
<style type="text/css" media="screen">
html, body { height:100%; }
body { margin:0; padding:0; overflow:auto; }
#flashContent { display:none; }
</style>
<link rel="stylesheet" type="text/css" href="#springUrl('')/res/js/flexpaper/css/flexpaper.css" />
<script type="text/javascript" src="#springUrl('')/res/js/flexpaper/jquery.min.js"></script>
<script type="text/javascript" src="#springUrl('')/res/js/flexpaper/flexpaper.js"></script>
<script type="text/javascript" src="#springUrl('')/res/js/flexpaper/flexpaper_handlers.js"></script>
</head>
<body>
<div style="margin:0px auto;">
<div id="documentViewer" class="flexpaper_viewer" style="width:1024px;height:600px;margin:0px auto;"></div>
<script type="text/javascript">
$('#documentViewer').FlexPaperViewer(
{ config : {
src : '#springUrl('')/res/js/flexpaper/FlexPaperViewer.swf',
SWFFile : '$!{swf_address}',
Scale : 0.6,
ZoomTransition : 'easeOut',
ZoomTime : 0.5,
ZoomInterval : 0.2,
FitPageOnLoad : true,
FitWidthOnLoad : false,
FullScreenAsMaxWindow : false,
ProgressiveLoading : false,
MinZoomSize : 0.2,
MaxZoomSize : 5,
SearchMatchAll : false,
InitViewMode : 'Portrait',
RenderingOrder : 'flash',
StartAtPage : '',
ViewModeToolsVisible : true,
ZoomToolsVisible : true,
NavToolsVisible : true,
CursorToolsVisible : true,
SearchToolsVisible : true,
WMode : 'window',
localeChain: 'zh_CN'
}}
);
</script>
</div>
</body>
</html>
其中需要注意的是flexpaper.js中使用的為相對路徑來指定 FlexPaperViewer.swf 文件的路徑。這里我把它改成絕對路徑了。
而config下的參數SWFFile則指定你的swf文件路徑即可完成播放。
實現效果
參考鏈接:
Java+FlexPaper+swfTools仿百度文庫文檔在線預覽系統設計與實現
附參考示例:
功能代碼
Aspose包
FlexPaper包