Java實(shí)現(xiàn)在線編輯預(yù)覽office文檔詳解
1 在線編輯
1.1 PageOffice簡(jiǎn)介
PageOffice是一款在線的office編輯軟件,幫助Web應(yīng)用系統(tǒng)或Web網(wǎng)站實(shí)現(xiàn)用戶在線編輯Word、Excel、PowerPoint文檔。可以完美實(shí)現(xiàn)在線公文流轉(zhuǎn),領(lǐng)導(dǎo)批閱,蓋章。可以給文件添加水印,在線安全預(yù)覽防止用戶下載和復(fù)制文件等
1.2 前端項(xiàng)目
由于pageoffice瀏覽器是ie內(nèi)核,vue3不兼容ie。所以需要把頁(yè)面放在后端
1.2.1 配置
在 vue.config.js 中配置代理
devServer: {
proxy: {
'/api': {
target: 'http://localhost:8081/samples-springboot-back', //"/api"對(duì)應(yīng)后端項(xiàng)目"http://localhost:8081/samples-springboot-back"地址
ws: true,
changeOrigin: true, // 允許跨域
pathRewrite: {
'^/api': '' // 標(biāo)識(shí)替換,使用 '/api' 代替真實(shí)的接口地址
}
}
}
}
1.2.2 頁(yè)面部分
在index.html頁(yè)面引用后端項(xiàng)目(samples-springboot-back)根目錄下的pageoffice.js
<script type="text/javascript" src="http://localhost:8081/samples-springboot-back/pageoffice.js"></script>
在index.vue頁(yè)面添加一個(gè)按鈕,調(diào)用POBrowser.openWindowModeless請(qǐng)求后端。http://localhost:8081/springboot-pageoffice-demo/SimpleWord/Word2 是后端打開文件的controller
POBrowser.openWindowModeless('http://localhost:8081/springboot-pageoffice-demo/SimpleWord/Word2', 'width=1150px;height=900px;');
在Word.vue頁(yè)面created中通過axios請(qǐng)求后臺(tái)獲取pageoffice控件(注意:后臺(tái)返回string字符串,前端需要使用v-html解析)
這里給后臺(tái)發(fā)請(qǐng)求的是axios,如果需要添加token可以在main.js中配置攔截器給請(qǐng)求添加token
Word.vue頁(yè)面,可以直接復(fù)制后修改url
<template>
<div class="Word">
<div style="height: 800px; width: auto" v-html="poHtmlCode" />
</div>
</template>
<script>
const axios = require("axios");
export default {
name: "Word",
data() {
return {
poHtmlCode: "",
};
},
created: function () {
axios
.post("/api/SimpleWord/Word")
.then((response) => {
this.poHtmlCode = response.data;
})
.catch(function (err) {
console.log(err);
});
},
methods: {
//控件中的一些常用方法都在這里調(diào)用,比如保存,打印等等
/**
* Save()方法是/api/SimpleWord/Word這個(gè)后臺(tái)controller中PageOfficeCtrl控件通過poCtrl.addCustomToolButton定義的方法,除了保存還有另存到本地、打印等功能。
*/
Save() {
document.getElementById("PageOfficeCtrl1").WebSave();
}
},
mounted: function () {
// 將PageOffice控件中的方法通過mounted掛載到window對(duì)象上,只有掛載后才能被vue組件識(shí)別
window.Save = this.Save;
},
};
</script>1.3 后端項(xiàng)目
1.3.1 pom.xml
<dependency>
<groupId>com.zhuozhengsoft</groupId>
<artifactId>pageoffice</artifactId>
<version>5.4.0.3</version>
</dependency>
1.3.2 添加配置
在啟動(dòng)類中配置servlet bean,poSysPath 是在 properites 中配置的磁盤路徑(注意:pageoffice的poserver.zz等這些請(qǐng)求不要攔截,get和post請(qǐng)求都放出來)
@Bean
public ServletRegistrationBean pageofficeRegistrationBean() {
com.zhuozhengsoft.pageoffice.poserver.Server poserver = new com.zhuozhengsoft.pageoffice.poserver.Server();
poserver.setSysPath(poSysPath);//設(shè)置PageOffice注冊(cè)成功后,license.lic文件存放的目錄
ServletRegistrationBean srb = new ServletRegistrationBean(poserver);
srb.addUrlMappings("/poserver.zz");
srb.addUrlMappings("/posetup.exe");
srb.addUrlMappings("/pageoffice.js");
srb.addUrlMappings("/jquery.min.js");
srb.addUrlMappings("/pobstyle.css");
srb.addUrlMappings("/sealsetup.exe");
return srb;
}
1.3.3 controller
打開文件的controller(webopen第一個(gè)參數(shù)是當(dāng)前文件的磁盤路徑,磁盤路徑必須反向雙斜杠)。
setServerPage和setSaveFilePage中的api是前端代理,前后端分離項(xiàng)目必須配置代理
@RestController
@RequestMapping(value = "/SimpleWord")
public class SimpleWordController {
@RequestMapping(value="/Word")
public String showWord(HttpServletRequest request) {
PageOfficeCtrl poCtrl = new PageOfficeCtrl(request);
poCtrl.setServerPage("/api/poserver.zz");//設(shè)置服務(wù)頁(yè)面
poCtrl.addCustomToolButton("保存", "Save", 1);
poCtrl.setSaveFilePage("/api/SimpleWord/save");//設(shè)置保存方法的url
//打開word
poCtrl.webOpen("D:\\doc\\test.docx", OpenModeType.docNormalEdit, "張三");
return poCtrl.getHtmlCode("PageOfficeCtrl1");
}
@RequestMapping("save")
public void save(HttpServletRequest request, HttpServletResponse response) {
FileSaver fs = new FileSaver(request, response);
fs.saveToFile("D:\\doc\\" + fs.getFileName());
fs.close();
}
}2 在線預(yù)覽
2.1 引言
最近遇到了文件預(yù)覽的需求,但一搜索發(fā)現(xiàn),這還不是一個(gè)簡(jiǎn)單的功能。于是又去查詢了很多資料,調(diào)研了一些方案,也踩了好多坑。最后總結(jié)方案如下:
花錢解決(使用市面上現(xiàn)有的文件預(yù)覽服務(wù))
微軟,google,阿里云 IMM,XDOC,Office Web 365,wps開放平臺(tái)
前端方案
pptx的預(yù)覽方案,pdf的預(yù)覽方案,docx的預(yù)覽方案,xlsx(excel)的預(yù)覽方案
服務(wù)端方案
openOffice,kkFileView,onlyOffice
2.2 市面上現(xiàn)有的文件預(yù)覽服務(wù)
2.2.1 微軟
docx,pptx,xlsx可以說是office三件套,那自然得看一下微軟官方提供的文件預(yù)覽服務(wù)。使用方法特別簡(jiǎn)單,只需要將文件鏈接,拼接到參數(shù)后面即可。
記得encodeURL
https://view.officeapps.live.com/op/view.aspx?src=${encodeURIComponent(url)}
對(duì)于docx,pptx,xlsx都有較好的支持,pdf不行。
還有一個(gè)坑點(diǎn)是:這個(gè)服務(wù)是否穩(wěn)定,有什么限制,是否收費(fèi),都查不到一個(gè)定論。在office官方網(wǎng)站上甚至找不到介紹這個(gè)東西的地方。
目前只能找到一個(gè)Q&A:
微軟官方人員回答表示:

翻譯翻譯,就是:幾乎永久使用,沒有收費(fèi)計(jì)劃,不會(huì)存儲(chǔ)預(yù)覽的文件數(shù)據(jù),限制文件10MB,建議用于 查看互聯(lián)網(wǎng)上公開的文件。
但經(jīng)過某些用戶測(cè)試發(fā)現(xiàn),使用了微軟的文件預(yù)覽服務(wù),然后刪除了文件地址,仍然可訪問,但過一段時(shí)間才會(huì)失效。
2.2.2 Google Drive查看器
接入簡(jiǎn)單,同 Office Web Viewer,只需要把 src 改為https://drive.google.com/viewer?url=${encodeURIComponent(url)}即可。
限制25MB,支持以下格式:

測(cè)試效果,支持docx,pptx,xlsx,pdf預(yù)覽,但pptx預(yù)覽的效果不如微軟,沒有動(dòng)畫效果,樣式有小部分會(huì)錯(cuò)亂。
2.2.3 阿里云 IMM

付費(fèi)使用
2.2.4 XDOC 文檔預(yù)覽
說了一些大廠的,在介紹一些其他的,需要自行分辨

2.2.5 Office Web 365
需要注意的是,雖然名字很像office,但我們看網(wǎng)頁(yè)的Copyright可以發(fā)現(xiàn),其實(shí)是一個(gè)西安的公司,不是微軟,但畢竟也提供了文件預(yù)覽的服務(wù)
官網(wǎng)地址:www.officeweb365.com/

2.2.6 WPS開放平臺(tái)
官方地址:solution.wps.cn

付費(fèi)使用,價(jià)格如下:

2.3 前端處理方案
2.3.1 pptx的預(yù)覽方案
先查一下有沒有現(xiàn)成的輪子,目前 pptx 的開源預(yù)覽方案能找到的只有這個(gè):github.com/g21589/PPTX…[6] 。但已經(jīng)六七年沒有更新,也沒有維護(hù),筆者使用的時(shí)候發(fā)現(xiàn)有很多兼容性問題。
簡(jiǎn)單來說就是,沒有。
對(duì)于這種情況,我們可以自行解析,主要步驟如下:
- 查詢pptx的國(guó)際標(biāo)準(zhǔn)
- 解析pptx文件
- 渲染成html或者canvas進(jìn)行展示
我們先去找一下pptx的國(guó)際標(biāo)準(zhǔn)
先解釋下什么是officeopenxml:
Office OpenXML,也稱為OpenXML或OOXML,是一種基于XML的辦公文檔格式,包括文字處理文檔、電子表格、演示文稿以及圖表、圖表、形狀和其他圖形材料。該規(guī)范由微軟開發(fā),并于2006年被ECMA國(guó)際采用為ECMA-376。第二個(gè)版本于2008年12月發(fā)布,第三個(gè)版本于2011年6月發(fā)布。該規(guī)范已被ISO和IEC采用為ISO/IEC 29500。
雖然Microsoft繼續(xù)支持較舊的二進(jìn)制格式(.doc、.xls和.ppt),但OOXML現(xiàn)在是所有Microsoft Office文檔(.docx、.xlsx和.pptx)的默認(rèn)格式。
由此可見,Office OpenXML由微軟開發(fā),目前已經(jīng)是國(guó)際標(biāo)準(zhǔn)。
接下來我們看一下pptx里面有哪些內(nèi)容,具體可以看pptx的官方標(biāo)準(zhǔn):officeopenxml-pptx[8]
PresentationML或.pptx文件是一個(gè)zip文件,其中包含許多“部分”(通常是UTF-8或UTF-16編碼)或XML文件。該包還可能包含其他媒體文件,例如圖像。該結(jié)構(gòu)根據(jù) OOXML 標(biāo)準(zhǔn) ECMA-376 第 2 部分中概述的開放打包約定進(jìn)行組織。
根據(jù)國(guó)際標(biāo)準(zhǔn),我們知道,pptx文件本質(zhì)就是一個(gè)zip文件,其中包含許多部分:
部件的數(shù)量和類型將根據(jù)演示文稿中的內(nèi)容而有所不同,但始終會(huì)有一個(gè) [Content_Types].xml、一個(gè)或多個(gè)關(guān)系 (.rels) 部件和一個(gè)演示文稿部件(演示文稿.xml),它位于 ppt 文件夾中,用于Microsoft Powerpoint 文件。通常,還將至少有一個(gè)幻燈片部件,以及一張母版幻燈片和一張版式幻燈片,從中形成幻燈片。
那么js如何讀取zip呢?
找到一個(gè)工具: www.npmjs.com
于是我們可以開始嘗試解析pptx了。
import JSZip from 'jszip' // 加載pptx數(shù)據(jù) const zip = await JSZip.loadAsync(pptxData)
解析[Content_Types].xml
每個(gè)pptx必然會(huì)有一個(gè) [Content_Types].xml。此文件包含包中部件的所有內(nèi)容類型的列表。每個(gè)部件及其類型都必須列在 [Content_Types].xml 中。通過它里面的內(nèi)容,可以解析其他的文件數(shù)據(jù)
const filesInfo = await getContentTypes(zip)
???????async function getContentTypes(zip: JSZip) {
const ContentTypesJson = await readXmlFile(zip, '[Content_Types].xml')
const subObj = ContentTypesJson['Types']['Override']
const slidesLocArray = []
const slideLayoutsLocArray = []
for (let i = 0; i < subObj.length; i++) {
switch (subObj[i]['attrs']['ContentType']) {
case 'application/vnd.openxmlformats-officedocument.presentationml.slide+xml':
slidesLocArray.push(subObj[i]['attrs']['PartName'].substr(1))
break
case 'application/vnd.openxmlformats-officedocument.presentationml.slideLayout+xml':
slideLayoutsLocArray.push(subObj[i]['attrs']['PartName'].substr(1))
break
default:
}
}
return {
slides: slidesLocArray,
slideLayouts: slideLayoutsLocArray,
}
}解析演示文稿
先獲取ppt目錄下的presentation.xml演示文稿的大小
由于演示文稿是xml格式,要真正的讀取內(nèi)容需要執(zhí)行 readXmlFile
const slideSize = await getSlideSize(zip)
async function getSlideSize(zip: JSZip) {
const content = await readXmlFile(zip, 'ppt/presentation.xml')
const sldSzAttrs = content['p:presentation']['p:sldSz']['attrs']
return {
width: (parseInt(sldSzAttrs['cx']) * 96) / 914400,
height: (parseInt(sldSzAttrs['cy']) * 96) / 914400,
}
}
加載主題
根據(jù) officeopenxml的標(biāo)準(zhǔn)解釋
每個(gè)包都包含一個(gè)關(guān)系部件,用于定義其他部件之間的關(guān)系以及與包外部資源的關(guān)系。這樣可以將關(guān)系與內(nèi)容分開,并且可以輕松地更改關(guān)系,而無(wú)需更改引用目標(biāo)的源。
除了包的關(guān)系部分之外,作為一個(gè)或多個(gè)關(guān)系源的每個(gè)部件都有自己的關(guān)系部分。每個(gè)這樣的關(guān)系部件都可以在部件的_rels子文件夾中找到,并通過在部件名稱后附加“.rels”來命名。
其中主題的相關(guān)信息就在ppt/_rels/presentation.xml.rels中
async function loadTheme(zip: JSZip) {
const preResContent = await readXmlFile(
zip,
'ppt/_rels/presentation.xml.rels',
)
const relationshipArray = preResContent['Relationships']['Relationship']
let themeURI
if (relationshipArray.constructor === Array) {
for (let i = 0; i < relationshipArray.length; i++) {
if (
relationshipArray[i]['attrs']['Type'] ===
'http://schemas.openxmlformats.org/officeDocument/2006/relationships/theme'
) {
themeURI = relationshipArray[i]['attrs']['Target']
break
}
}
} else if (
relationshipArray['attrs']['Type'] ===
'http://schemas.openxmlformats.org/officeDocument/2006/relationships/theme'
) {
themeURI = relationshipArray['attrs']['Target']
}
if (themeURI === undefined) {
throw Error("Can't open theme file.")
}
??????? return readXmlFile(zip, 'ppt/' + themeURI)
}2.3.2 pdf的預(yù)覽方案
2.3.2.1 iframe和embed
pdf 比較特別,一般的瀏覽器默認(rèn)支持預(yù)覽pdf。因此,我們可以使用瀏覽器的能力:<iframe src="viewFileUrl" />
但這樣就完全依賴瀏覽器,對(duì)PDF的展示,交互,是否支持全看瀏覽器的能力,且不同的瀏覽器展示和交互往往不同,如果需要統(tǒng)一的話,最好還是嘗試其他方案。
embed的解析方式也是一樣,這里不舉例子了
2.3.2.2 pdfjs
由mozilla出品,就是我們常見的MDN的老大。而且目前 火狐瀏覽器 使用的 PDF 預(yù)覽就是采用這個(gè),我們可以用火狐瀏覽器打開pdf文件,查看瀏覽器使用的js就能發(fā)現(xiàn)
需要注意的是,最新版 pdf.js 限制了 node 版本,需要大于等于18
如果你項(xiàng)目node版本小于這個(gè)情況,可能會(huì)無(wú)法使用。
具體使用情況如下:
import * as pdfjs from 'pdfjs-dist'
import * as pdfjsWorker from 'pdfjs-dist/build/pdf.work.entry'
interface Viewport {
width: number
height: number
viewBox: Array<number>
}
interface RenderContext {
canvasContext: CanvasRenderingContext2D | null
transform: Array<number>
viewport: Viewport
}
interface PDFPageProxy {
pageNumber: number
getViewport: () => Viewport
render: (options: RenderContext) => void
}
interface PDFDocumentProxy {
numPages: number
getPage: (x: number) => Promise<PDFPageProxy>
}
class PdfPreview {
private pdfDoc: PDFDocumentProxy | undefined
pageNumber: number
total: number
dom: HTMLElement
pdf: string | ArrayBuffer
constructor(pdf: string | ArrayBuffer, dom: HTMLElement | undefined) {
this.pageNumber = 1
this.total = 0
this.pdfDoc = undefined
this.pdf = pdf
this.dom = dom ? dom : document.body
}
private getPdfPage = (number: number) => {
return new Promise((resolve, reject) => {
if (this.pdfDoc) {
this.pdfDoc.getPage(number).then((page: PDFPageProxy) => {
const viewport = page.getViewport()
const canvas = document.createElement('canvas')
this.dom.appendChild(canvas)
const context = canvas.getContext('2d')
const [_, __, width, height] = viewport.viewBox
canvas.width = width
canvas.height = height
viewport.width = width
viewport.height = height
canvas.style.width = Math.floor(viewport.width) + 'px'
canvas.style.height = Math.floor(viewport.height) + 'px'
const renderContext = {
canvasContext: context,
viewport: viewport,
transform: [1, 0, 0, -1, 0, viewport.height],
}
page.render(renderContext)
resolve({ success: true, data: page })
})
} else {
reject({ success: false, data: null, message: 'pdfDoc is undefined' })
}
})
}
pdfPreview = () => {
window.pdfjsLib.GlobalWorkerOptions.workerSrc = pdfjsWorker
window.pdfjsLib
.getDocument(this.pdf)
.promise.then(async (doc: PDFDocumentProxy) => {
this.pdfDoc = doc
this.total = doc.numPages
for (let i = 1; i <= this.total; i++) {
await this.getPdfPage(i)
}
})
}
prevPage = () => {
if (this.pageNumber > 1) {
this.pageNumber -= 1
} else {
this.pageNumber = 1
}
this.getPdfPage(this.pageNumber)
}
nextPage = () => {
if (this.pageNumber < this.total) {
this.pageNumber += 1
} else {
this.pageNumber = this.total
}
this.getPdfPage(this.pageNumber)
}
}
const createReader = (file: File): Promise<string | ArrayBuffer | null> => {
return new Promise((resolve, reject) => {
const reader = new FileReader()
reader.readAsDataURL(file)
reader.onload = () => {
resolve(reader.result)
}
reader.onerror = (error) => {
reject(error)
}
reader.onabort = (abort) => {
reject(abort)
}
})
}
export const renderPdf = async (
file: File,
dom?: HTMLElement,
): Promise<void> => {
try {
if (typeof window !== 'undefined') {
const pdf = await createReader(file)
if (pdf) {
const PDF = new PdfPreview(pdf, dom)
PDF.pdfPreview()
}
}
} catch (error) {
console.log('renderPdf', error)
}
}
2.3.3 docx的預(yù)覽方案
我們可以去查看docx的國(guó)際標(biāo)準(zhǔn),去解析文件格式,渲染成html和canvas,不過比較好的是,已經(jīng)有人這么做了,還開源了
使用方法如下:
import { renderAsync } from 'docx-preview'
interface DocxOptions {
bodyContainer?: HTMLElement | null
styleContainer?: HTMLElement
buffer: Blob
docxOptions?: Partial<Record<string, string | boolean>>
}
export const renderDocx = (options: DocxOptions): Promise<void> | undefined => {
if (typeof window !== 'undefined') {
const { bodyContainer, styleContainer, buffer, docxOptions = {} } = options
const defaultOptions = {
className: 'docx',
ignoreLastRenderedPageBreak: false,
}
const configuration = Object.assign({}, defaultOptions, docxOptions)
if (bodyContainer) {
return renderAsync(buffer, bodyContainer, styleContainer, configuration)
} else {
const contain = document.createElement('div')
document.body.appendChild(contain)
return renderAsync(buffer, contain, styleContainer, configuration)
}
}
}
2.3.4 前端預(yù)覽方案總結(jié)
我們對(duì)以上找到的優(yōu)秀的解決方案,進(jìn)行改進(jìn)和總結(jié),并封裝成一個(gè)web components組件:preview組件
為什么是web components組件?
因?yàn)樗蚣軣o(wú)關(guān),可以在任何框架中使用,且使用起來跟原生的div標(biāo)簽一樣方便。并編寫使用文檔: preview組件文檔, 文檔支持交互體驗(yàn)。
目前docx,pdf,xlsx預(yù)覽基本可以了,都是最好的方案。pptx預(yù)覽效果不太好,因?yàn)樾枰孕薪馕觥?/p>
2.4 服務(wù)端預(yù)覽方案
2.4.1 openOffice
由于瀏覽器不能直接打開 docx,pptx,xlsx 等格式文件,但可以直接打開pdf和圖片,因此,我們可以換一個(gè)思路,用服務(wù)端去轉(zhuǎn)換下文件的格式,轉(zhuǎn)換成瀏覽器能識(shí)別的格式,然后再讓瀏覽器打開,這不就OK了嗎,甚至不需要前端處理了。
我們可以借助openOffice的能力,先介紹一下openOffice:
Apache OpenOffice是領(lǐng)先的開源辦公軟件套件,用于文字處理,電子表格,演示文稿,圖形,數(shù)據(jù)庫(kù)等。它有多種語(yǔ)言版本,適用于所有常用計(jì)算機(jī)。它以國(guó)際開放標(biāo)準(zhǔn)格式存儲(chǔ)您的所有數(shù)據(jù),還可以從其他常見的辦公軟件包中讀取和寫入文件。它可以出于任何目的完全免費(fèi)下載和使用。
官網(wǎng)如下:www.openoffice.org
完整示例如下:
package org.example;
import org.artofsolving.jodconverter.OfficeDocumentConverter;
import org.artofsolving.jodconverter.office.DefaultOfficeManagerConfiguration;
import org.artofsolving.jodconverter.office.OfficeManager;
import java.io.File;
public class OfficeUtil {
private static OfficeManager officeManager;
private static int port[] = {8100};
/**
* start openOffice service.
*/
public static void startService() {
DefaultOfficeManagerConfiguration configuration = new DefaultOfficeManagerConfiguration();
try {
System.out.println("準(zhǔn)備啟動(dòng)office轉(zhuǎn)換服務(wù)....");
configuration.setOfficeHome("這里的路徑一般為C:\\Program Files (x86)\\OpenOffice 4的bin目錄");
configuration.setPortNumbers(port); // 設(shè)置轉(zhuǎn)換端口,默認(rèn)為8100
configuration.setTaskExecutionTimeout(1000 * 60 * 30L);// 設(shè)置任務(wù)執(zhí)行超時(shí)為30分鐘
configuration.setTaskQueueTimeout(1000 * 60 * 60 * 24L);// 設(shè)置任務(wù)隊(duì)列超時(shí)為24小時(shí)
officeManager = configuration.buildOfficeManager();
officeManager.start(); // 啟動(dòng)服務(wù)
System.out.println("office轉(zhuǎn)換服務(wù)啟動(dòng)成功!");
} catch (Exception e) {
System.out.println("office轉(zhuǎn)換服務(wù)啟動(dòng)失敗!詳細(xì)信息:" + e);
}
}
/**
* stop openOffice service.
*/
public static void stopService() {
System.out.println("準(zhǔn)備關(guān)閉office轉(zhuǎn)換服務(wù)....");
if (officeManager != null) {
officeManager.stop();
}
System.out.println("office轉(zhuǎn)換服務(wù)關(guān)閉成功!");
}
public static void convertToPDF(String inputFile, String outputFile) {
startService();
System.out.println("進(jìn)行文檔轉(zhuǎn)換轉(zhuǎn)換:" + inputFile + " --> " + outputFile);
OfficeDocumentConverter converter = new OfficeDocumentConverter(officeManager);
converter.convert(new File(inputFile), new File(outputFile));
stopService();
}
public static void main(String[] args) {
convertToPDF("/Users/koolearn/Desktop/asdf.docx", "/Users/koolearn/Desktop/adsf.pdf");
}
}
2.4.2 kkFileView
支持的文件預(yù)覽格式非常豐富圖片

安裝下libreoffice :
kkFileView明確要求的額外依賴 libreoffice,否則無(wú)法啟動(dòng)
啟動(dòng)項(xiàng)目
找到主文件,主函數(shù)mian,即可執(zhí)行
2.4.3 onlyOffice
官網(wǎng)地址:https://www.onlyoffice.com/zh
開發(fā)者版本和社區(qū)版免費(fèi),企業(yè)版付費(fèi):www.onlyoffice.com/zh/docs-ent
預(yù)覽的文件種類沒有kkFileView多,但對(duì)office三件套有很好的支持,甚至支持多人編輯。
以上就是Java實(shí)現(xiàn)在線編輯預(yù)覽office文檔詳解的詳細(xì)內(nèi)容,更多關(guān)于Java在線預(yù)覽office的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
Spring Cloud Config實(shí)現(xiàn)分布式配置中心
這篇文章主要介紹了Spring Cloud Config實(shí)現(xiàn)分布式配置中心,小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧2018-04-04
MyBatisPlus中使用or()和and()遇到的問題及細(xì)節(jié)處理
這篇文章主要介紹了MyBatisPlus中使用or()和and()遇到的問題,本文通過多種寫法實(shí)例代碼相結(jié)合給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2020-08-08
springboot+mybaties項(xiàng)目中掃描不到@mapper注解的解決方法
本文主要介紹了springboot+mybaties項(xiàng)目中掃描不到@mapper注解的解決方法,該報(bào)錯(cuò)表明掃描不到Mapper層,具有一定的參考價(jià)值,感興趣的可以了解一下2024-05-05
java實(shí)現(xiàn)簡(jiǎn)單的學(xué)生信息管理系統(tǒng)代碼實(shí)例
這篇文章主要介紹了java實(shí)現(xiàn)簡(jiǎn)單的學(xué)生信息管理系統(tǒng),文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2019-04-04
java自帶命令行工具jmap、jhat與jinfo的使用實(shí)例代碼詳解
本篇文章主要通過代碼實(shí)例對(duì)java自帶命令行工具jmap、jhat與jinfo的使用做出了詳解,需要的朋友可以參考下2017-04-04

