需求
文档在线预览功能,类似于vue-office-docx这样的插件,由于doc格式不兼容,所以体验效果不好,退而求其次,把所有文档(doc、ppt、xls等)全部在线转为PDF,再由前端接收并展示。
解决方案
使用 liberoffice 命令行将doc等文档转换为pdf文件并另存为到临时文件,vue前端接收pdf文件流并用vue-office-pdf插件完成预览。
文末提供更简便方法~
后台
pom
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-exec</artifactId>
<version>1.3</version>
</dependency>
controller
/**
* 转 pdf 下载
* @param ossId
* @param response
* @throws IOException
*/
@GetMapping("/downloadPdf/{ossId}")
public void downloadPdf(@PathVariable Long ossId, HttpServletResponse response) throws IOException {
iSysOssService.downloadToPdf(ossId,response);
}
service
import org.apache.commons.exec.CommandLine;
import org.apache.commons.exec.DefaultExecutor;
import org.apache.commons.exec.ExecuteException;
import org.apache.commons.exec.ExecuteResultHandler;
import org.apache.xmlbeans.impl.common.IOUtil;
import java.util.concurrent.Semaphore;
/**
* 转pdf 下载
* @param ossId
* @param response
* @throws IOException
*/
public void downloadToPdf(Long ossId, HttpServletResponse response) throws IOException {
SysOssVo sysOss = SpringUtils.getAopProxy(this).getById(ossId);
if (ObjectUtil.isNull(sysOss)) {
throw new ServiceException("文件数据不存在!");
}
String pdfName = sysOss.getOriginalName().substring(0, sysOss.getOriginalName().indexOf(".")) + ".pdf";
FileUtils.setAttachmentResponseHeader(response, pdfName);
response.setContentType(MediaType.APPLICATION_OCTET_STREAM_VALUE + "; charset=UTF-8");
try {
DefaultExecutor exec = new DefaultExecutor();
File tempFolder = new File(System.getProperty("java.io.tmpdir"), "office2pdf-" + IdUtil.fastSimpleUUID());
if (!tempFolder.exists()) {
tempFolder.mkdirs();
}
String orginName = tempFolder.getAbsolutePath() + File.separator + sysOss.getOriginalName();
//复制 本地
OssClient storage = OssFactory.instance();
try(InputStream inputStream = storage.getObjectContent(sysOss.getUrl())) {
int available = inputStream.available();
OutputStream fileOutputStream = new FileOutputStream(orginName);
IoUtil.copy(inputStream, fileOutputStream, available);
} catch (Exception e) {
throw new ServiceException(e.getMessage());
}
// 同步等待
Semaphore semaphore = new Semaphore(1);
semaphore.acquire();
ExecuteResultHandler erh = new ExecuteResultHandler() {
@Override
public void onProcessComplete(int i) {
semaphore.release();
//转换完成逻辑
}
@Override
public void onProcessFailed(ExecuteException e) {
semaphore.release();
}
};
String command = "soffice --invisible --convert-to pdf --outdir \"" + tempFolder.getAbsolutePath() + "\" \""
+ orginName + "\"";
exec.execute(CommandLine.parse(command), erh);
// 等待执行完成
semaphore.acquire();
//response 下载
File file = new File(tempFolder.getAbsolutePath() + File.separator + pdfName);
FileInputStream inputStream = new FileInputStream(file);
int available = inputStream.available();
IoUtil.copy(inputStream, response.getOutputStream(), available);
response.setContentLength(available);
} catch (Exception e) {
throw new ServiceException(e.getMessage());
}
}
前台
// pdf文档预览组件
npm install @vue-office/pdf
<template>
<!-- pdf 直接预览 -->
<div>
<vue-office-pdf
:src="pdfUrl"
@rendered="renderedHandler"
@error="errorHandler"
/>
</div>
</template>
//引入VueOfficePdf组件
import VueOfficePdf from '@vue-office/pdf'
export default {
components:{
VueOfficePdf,
},
data() {
return {
pdfUrl: "",
fullscreenLoading: false,
}
},
methods: {
/** pdf */
renderedHandler() {
console.log("渲染完成")
this.fullscreenLoading = false
},
errorHandler() {
console.log("渲染失败")
this.fullscreenLoading = false
},
/** 在线转PDF */
ossPdf(ossId) {
this.fullscreenLoading = true
var url = process.env.VUE_APP_BASE_API + '/file/downloadPdf/' + ossId
axios({
method: 'get',
url: url,
responseType: 'blob',
headers: { 'Authorization': 'Bearer ' + getToken() }
}).then((res) => {
const isBlob = blobValidate(res.data);
if (isBlob) {
let fileURL = null
var blob = new Blob([res.data], {type: 'application/pdf'})
if (window.createObjectURL != undefined) { // basic
fileURL = window.createObjectURL(blob);
}else if (window.webkitURL != undefined) { // webkit or chrome
try {
fileURL = window.webkitURL.createObjectURL(blob);
} catch (error) {console.log(error)}
} else if (window.URL != undefined) { // mozilla(firefox)
try {
fileURL = window.URL.createObjectURL(blob);
} catch (error) {console.log(error)}
}
console.log("PDF文件地址0 ", fileURL)
this.pdfUrl = fileURL
} else {
this.printErrMsg(res.data);
}
}).catch((r) => {
console.error(r)
Message.error('下载文件出现错误,请联系管理员!')
})
},
}
扩展
该项目使用流行的spring boot搭建,易上手和部署,基本支持主流办公文档的在线预览,如doc,docx,xls,xlsx,ppt,pptx,pdf,txt,zip,rar,图片,视频,音频等等
docker安装并运行
需要预览文件时,只需要调用浏览器打开本项目的预览接口,并传入须要预览文件的url