基于Vue的原生OnlyOffice的使用
4、部署到线上环境时,如果是https的环境,一般项目都会有网关设置,我们的保存接口要走网关的话,需要开网关白名单,并且要去掉ssl的校验(修改onlyoffice的默认配置,既容器中的default.json中的配置),这样文件才能正常编辑后保存。https环境上需要设置证书,测试环境用的是中台自创建的证书,可参考:https://huaweicloud.csdn.net/638db1cedac
·
- OnlyOffice服务端进行docker部署(http环境,默认80端口,映射出9999端口)
docker run -i -t -d --name onlyoffice --privileged -p 9999:80 -p 5432:5432 --restart=always -v /e/publish/onlyoffice/DocumentServer/logs:/var/log/onlyoffice -v /e/publish/onlyoffice/DocumentServer/data:/var/www/onlyoffice/Data -v /e/publish/onlyoffice/DocumentServer/lib:/var/lib/onlyoffice -v /e/publish/onlyoffice/DocumentServer/db:/var/lib/postgresql -v /e/work/gznew/seal:/var/www/onlyoffice/documentserver/sdkjs-plugins/seal tps.harbor.elextec.com/library/onlyoffice_documentserver:v1.0.0
1.1 在https环境上进行部署
https环境上需要设置证书,测试环境用的是中台自创建的证书,可参考:https://huaweicloud.csdn.net/638db1cedacf622b8df8c703.html,若是部署到生产环境,需要购买证书。
docker run -it -d -p 1443:443 --restart=always \
-v /zhiku_dev/server/onlyoffice/logs:/var/log/onlyoffice \
-v /zhiku_dev/server/onlyoffice/data:/var/www/onlyoffice/Data \
-v /zhiku_dev/server/onlyoffice/lib:/var/lib/onlyoffice \
-v /zhiku_dev/server/onlyoffice/db:/var/lib/postgresql -e JWT_ENABLED=false onlyoffice/documentserver
- 前端修改index.html,并引入api.js
http的:
<script type="text/javascript" src="http://172.16.200.244:9999/web-apps/apps/api/documents/api.js"></script>
https的,如果是不能写死的就需要自己获取协议,ip,拼接路径。
<script type="text/javascript" src="https://172.16.200.118:1443/web-apps/apps/api/documents/api.js"></script>
- 前端新增公共组件onlyOffice.vue
<!--onlyoffice 编辑器-->
<template>
<div
id="editorDiv"
ref="editorDiv"
/>
</template>
<script>
export default {
name: 'Editor',
props: {
option: {
type: Object,
default: () => {
return {}
}
}
},
data () {
return {
doctype: '',
docEditor: null
}
},
watch: {
option: {
handler: function (n, o) {
this.setEditor(n)
this.doctype = this.handleDocType(n.fileType)
},
deep: true
}
},
mounted () {
if (this.option.url) {
console.log('url:' + this.option.url)
this.setEditor(this.option)
}
},
methods: {
handleDocType (fileType) {
let docType = ''
let fileTypesDoc = [
'doc', 'docm', 'docx', 'dot', 'dotm', 'dotx', 'epub', 'fodt', 'htm', 'html', 'mht', 'odt', 'ott', 'pdf', 'rtf', 'txt', 'djvu', 'xps'
]
let fileTypesCsv = [
'csv', 'fods', 'ods', 'ots', 'xls', 'xlsm', 'xlsx', 'xlt', 'xltm', 'xltx'
]
let fileTypesPPt = [
'fodp', 'odp', 'otp', 'pot', 'potm', 'potx', 'pps', 'ppsm', 'ppsx', 'ppt', 'pptm', 'pptx'
]
if (fileTypesDoc.includes(fileType)) {
docType = 'text'
}
if (fileTypesCsv.includes(fileType)) {
docType = 'spreadsheet'
}
if (fileTypesPPt.includes(fileType)) {
docType = 'presentation'
}
return docType
},
// 缓存到onlyOffice数据库里面的回调
onDocumentStateChange (event) {
if (event.data) {
console.log('文档变更了')
console.log(event)
} else {
console.log('文档没有变更,Changes are collected on document editing service')
}
this.$emit('fileIsChanged', event.data)
},
// 点击保存按钮的回调
onRequestSaveAs (event) {
let fileType = event.data.fileType
let title = event.data.title
let url = event.data.url
console.log(fileType)
console.log(title)
console.log(url)
},
// 下载另存为
onDownloadAs (event) {
let url = event.data.url
console.log('ONLYOFFICE Document Editor create file: ' + url)
},
onDocumentReady (event) {
console.log('onDocumentReady')
},
onAppReady (event) {
console.log('onappready')
},
// onRequestCompareFile () {
// console.log('this.option.originUrl: ', this.option.originUrl)
// this.docEditor.setRevisedFile({
// 'fileType': 'docx',
// 'url': this.option.originUrl || 'http://172.16.200.118:39501/zhiku-files/20230410/0fdf2ebf66124d19b5cbef9c94e49fe9_1.docx?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Credential=minio%2F20230425%2Fus-east-1%2Fs3%2Faws4_request&X-Amz-Date=20230425T102430Z&X-Amz-Expires=604800&X-Amz-SignedHeaders=host&X-Amz-Signature=93d58499e430a6677d5fc3d9268701ddc026374d0d64d8edaac55646fb9eecb1'
// })
// },
setEditor (option) {
// 已经初始化过实例就直接更新文档链接 - 没生效
// if (this.docEditor) this.docEditor.setReferenceData({ 'url': option.url })
this.doctype = this.handleDocType(option.fileType)
let config = {
document: {
fileType: option.fileType,
key: option.key,
title: option.title,
permissions: {
comment: true,
download: true,
modifyContentControl: true,
modifyFilter: true,
print: false,
edit: option.isEdit,
fillForms: true,
review: true // 第一是否显示审阅文档菜单
},
url: option.url
},
documentType: this.doctype,
editorConfig: {
callbackUrl: option.editUrl,
lang: 'zh-CN',
customization: {
hideRightMenu: false, // 是否显示右侧菜单
forcesave: true,
autosave: false, // 是否自动保存
commentAuthorOnly: false,
comments: true,
compactHeader: false,
compactToolbar: true,
feedback: false,
plugins: true,
showReviewChanges: !option.user.isOperation, // 定义在加载编辑器时是否自动显示或隐藏审阅更改面板。默认值为false
spellcheck: false, // 关闭拼写检查
review: {
hideReviewDisplay: true, // 定义显示模式按钮是在协作选项卡上显示还是隐藏
reviewDisplay: 'view', // 定义打开文档进行查看时将使用的审阅编辑模式
trackChanges: true, // true为对自己启动,false为对自己关闭。跟踪变化对自己启动,记录自己的修改。其他人也可以查看在什么时间谁修改的文件的哪部分内容,就是协作功能
hoverMode: true // 定义检查显示模式:通过悬停更改(true)在工具提示中显示检查,或者通过单击更改(false)在气泡中显示检查。默认值为false。
}
},
user: {
id: option.user.id,
name: option.user.name
},
mode: 'edit' // 文档操作模式 view 视图模式不可编辑 edit 编辑模式可编辑文档
},
width: '100%',
height: '100%',
token: option.token,
events: {
'onAppReady': this.onAppReady,
'onDocumentStateChange': this.onDocumentStateChange, // 修改文档时调用的函数
'onRequestSaveAs': this.onRequestSaveAs, // 保存按钮回调 save-copy-as
'onDownloadAs': this.onDownloadAs, // 下载另存为
'onDocumentReady': this.onDocumentReady // 文档加载完成
// 'onRequestCompareFile': this.onRequestCompareFile // 对比文件
}
}
if (this.docEditor) this.docEditor.destroyEditor()
// eslint-disable-next-line no-undef
this.docEditor = new DocsAPI.DocEditor('editorDiv', config)
console.log('编辑器:', this.docEditor)
}
}
}
</script>
<style scoped lang="scss">
</style>
- 编辑后服务端存储文档
package com.elex.platform.tps.besrss.controller.office;
import io.swagger.annotations.Api;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;
import sun.net.www.protocol.http.HttpURLConnection;
import java.io.*;
import java.net.URL;
import java.util.HashMap;
import java.util.Map;
@RestController
@Slf4j
@Api(tags = "OnlyOffice")
public class OnlyOfficeController {
@PostMapping("/save/{fileId}/{fileName}/{fileSuffix}")
public ResponseEntity<Map> save(@RequestBody Map param
, @PathVariable String fileId
, @PathVariable String fileName
, @PathVariable String fileSuffix
) {
log.info("method[save] fileId:{}", fileId);
Integer status = (Integer) (param.get("status"));
// log.info("method[save] param:{}", JsonUtils.toJson(param));
//文档主键
// Long id = MapUtil.getLong(param, "key");
// log.info("method[save] id:{}", id);
//操作状态 1:编辑 2:准备保存 3: 保存错误 4:文档关闭没有修改 6: 正在编辑文档,但保存了当前文档状态 7:强制保存文档时发生错误
// Integer status = MapUtil.getInteger(param, "status", -1);
// if (status == 2) {
//当前在操作用户id
// List<Long> usersId = MapUtil.getValueAsList(param, "users");
// log.info("method[save] usersId:{}", usersId.toString());
// }
if (status == 6) {
//TODO 可以利用websocket 推送到页面 产生提示语
String url = (String) (param.get("url"));
// log.info("method[save] 正在编辑文档,但保存了当前文档状态. url:{}", MapUtil.getString(param, "url"));
saveUrlAs(url,"d:/","GET",fileName,fileSuffix);
Map result = new HashMap(1);
result.put("error", "0");
result.put("msg", "操作成功");
return ResponseEntity.ok(result);
}
Map result = new HashMap(1);
result.put("error", "0");
return ResponseEntity.ok(result);
}
public static File saveUrlAs(String url, String filePath, String method, String fileName
, String fileSuffix
) {
//System.out.println("fileName---->"+filePath);
//创建不同的文件夹目录
File file = new File(filePath);
//判断文件夹是否存在
if (!file.exists()) {
//如果文件夹不存在,则创建新的的文件夹
file.mkdirs();
}
FileOutputStream fileOut = null;
HttpURLConnection conn = null;
InputStream inputStream = null;
try {
// 建立链接
URL httpUrl = new URL(url);
conn = (HttpURLConnection) httpUrl.openConnection();
//以Post方式提交表单,默认get方式
conn.setRequestMethod(method);
conn.setDoInput(true);
conn.setDoOutput(true);
// post方式不能使用缓存
conn.setUseCaches(false);
//连接指定的资源
conn.connect();
//获取网络输入流
inputStream = conn.getInputStream();
BufferedInputStream bis = new BufferedInputStream(inputStream);
//判断文件的保存路径后面是否以/结尾
if (!filePath.endsWith("/")) {
filePath += "/";
}
//写入到文件(注意文件保存路径的后面一定要加上文件的名称)
fileOut = new FileOutputStream(filePath + fileName + "." + fileSuffix );
BufferedOutputStream bos = new BufferedOutputStream(fileOut);
byte[] buf = new byte[4096];
int length = bis.read(buf);
//保存文件
while (length != -1) {
bos.write(buf, 0, length);
length = bis.read(buf);
}
bos.close();
bis.close();
conn.disconnect();
} catch (Exception e) {
e.printStackTrace();
System.out.println("抛出异常!!");
}
return file;
}
}
5.1、编辑后服务端存储文档 – 另一种存储实例,文件存储在minio
@ApiOperation("前端保存的回调地址,用于保存文件流到minIO")
@ApiOperationSupport(order = 8)
@PostMapping("/wordFileCallBack/{recordCode}/{pageNum}/{userId}")
public ResponseEntity<Map> wordFileCallBack(@RequestBody Map param,
@ApiParam(name = "recordCode", value = "文件code") @PathVariable String recordCode, @ApiParam(name = "pageNum", value = "当前页码") @PathVariable Integer pageNum,@ApiParam(name = "userId", value = "用户ID") @PathVariable Integer userId) throws InterruptedException {
//1-操作业务 进操作记录表并将zhiku_attachment_assignment任务的状态置进行中3即可(OpinionConstants中有)。
Integer status = (Integer) (param.get("status"));
log.info("===>param:"+param);
if(status!=null&&status==6){
ZhikuEditLog zhikuEditLog = new ZhikuEditLog();
zhikuEditLog.setOperatorPage(String.valueOf(pageNum));
zhikuEditLog.setRecordCode(recordCode);
collegeFacade.saveEditLogUpdateCheckStatus(zhikuEditLog,userId);
//2-保存了文件,将office服务器的word文件传到minIO里面
Map<String, String> filePath = attachmentFacade.getFilePath(recordCode);
String wordFileName = MapUtil.getStr(filePath,"transFilePath").replaceAll(".pdf",".docx");
String url = (String) (param.get("url"));
minioHelper.replaceWordFile(pageNum,url,wordFileName);
}
Map result = new HashMap(1);
result.put("error", "0"); // 比如返回error:0 这种结构,不然onlyoffice编辑器中会出现错误
return ResponseEntity.ok(result);
}
public void replaceWordFile(Integer pageNum,String url,String wordFileName) throws InterruptedException {
//1-先找到onlyoffice服务器里面的文件
//wordFileName 的格式是 20230410/123123121.docx
wordFileName = wordFileName.replaceAll(".docx","_"+pageNum+".docx");
//服务器里面有,不用下载
//File file = saveUrlAs(url, wordFileName);
String[] split = url.split("/");
String fileUrl = split[6]+"/"+split[7];
fileUrl = "/root/onlyoffice/data/"+fileUrl;
log.error("===>服务器上的URL是:"+fileUrl);
File file = new File(fileUrl);
long fileSize = 0;
while(true){
if (file.exists() && fileSize > 0 && fileSize == FileUtil.size(file)) {
break;
}
Thread.sleep(150);
fileSize = FileUtil.size(file);
log.error("===>getCanonicalPath:"+FileUtil.getCanonicalPath(file));
}
log.error("===>跳出循环了");
//将这个文件传到minIO里面,路径要一样,用于替换之前的文件
if(checkFile(wordFileName)){
remove(wordFileName);
}
uploadFile(file,wordFileName);
}
@SneakyThrows
public String uploadFile(File file,String wordFileName) {
String contentType = MimetypesFileTypeMap.getDefaultFileTypeMap().getContentType(file);
InputStream inputStream = new FileInputStream(file);
ObjectWriteResponse objectWriteResponse = minioClient.putObject(PutObjectArgs.builder()
.bucket(minioConfig.getBucket())
.object(wordFileName)
.contentType(contentType)
.stream(inputStream, inputStream.available(), -1)
.build());
inputStream.close();
return objectWriteResponse.object();
}
- 注意事项
1、onlyoffice使用docker安装时,默认会安装最新版本,本次安装的是7.2版本,JWT令牌认证默认会打开的,我们没有令牌,需要将这个配置关闭,不然就算成功打开文档也会如下报错,如何关闭, 可以参考:https://helpcenter.onlyoffice.com/installation/docs-configure-jwt.aspx
这个设置重启onlyoffice服务会被恢复,请注意。
2、遇到过文件下载失败的问题,这个就要确认后端提供的可访问的文件url在浏览器中能否正常下载,若是也不行,就是文件路径本身有问题,应该考虑是否映射成功,或者文件本身权限问题。
3、保存文件的回调路径editUrl一定要设置正确,前后端得对应上,不然打开文档之后就一直会报文件保存失败。
4、部署到线上环境时,如果是https的环境,一般项目都会有网关设置,我们的保存接口要走网关的话,需要开网关白名单,并且要去掉ssl的校验(修改onlyoffice的默认配置,既容器中的default.json中的配置),这样文件才能正常编辑后保存。
更多推荐
已为社区贡献5条内容
所有评论(0)