技术标签: java vue+onlyoffice vue-js 在线文档编辑
完成了springboot+vue+onlyoffice的集成,实现用户上传文件,编辑文件的基本操作。
后续将完成协作编辑,版本管理,文件加密,解密打开等相关操作。
文件界面实例图:
1、部署onlyoffice的docker
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 onlyoffice/documentserver
-v /e/work/gznew/seal:/var/www/onlyoffice/documentserver/sdkjs-plugins/seal 是将自己写的签章插件挂载到插件目录
2、编辑器控件
<!--onlyoffice 编辑器-->
<template>
<div id="editorDiv"></div>
</template>
<script>
import {
handleDocType } from '@/utils/onlyofficeUtil'
export default {
name: 'editor',
props: {
option: {
type: Object,
default: () => {
return {
}
}
}
},
data() {
return {
doctype: ''
}
},
mounted() {
if (this.option.url) {
this.setEditor(this.option)
}
},
methods: {
setEditor(option) {
this.doctype = 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',
customization: {
commentAuthorOnly: false,
comments: true,
compactHeader:false,
compactToolbar:true,
feedback:false,
plugins:true
},
user:{
id:option.user.id,
name:option.user.name
},
mode:option.model?option.model:'edit',
},
width: '100%',
height: '100%',
token:option.token
}
// console.log(config)
let docEditor = new DocsAPI.DocEditor('editorDiv', config)
},
},
watch: {
option: {
handler: function (n, o) {
this.setEditor(n)
this.doctype = handleDocType(n.fileType)
},
deep: true,
}
}
}
</script>
<style scoped>
</style>
3、控件中用到的方法,判断文件类型
export function 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;
}
4、用一个控件来引用编辑器控件,接收文件参数,并组织编辑器控件参数
<template>
<editor :option="option"></editor>
</template>
<script>
import {
getAction } from '@/api/manage'
import editor from './editor'
import Vue from 'vue'
import {
ACCESS_TOKEN, USER_INFO } from '@/store/mutation-types'
export default {
name: 'fView',
components: {
editor },
data() {
return {
id: this.$route.params.id,
option: {
url: '',
isEdit: true,
fileType: '',
title: '',
token: Vue.ls.get(ACCESS_TOKEN),
user: {
id: '',
name: ''
},
mode: 'edit',
editUrl: '',
key: ''
},
}
},
created() {
let userInfo = Vue.ls.get(USER_INFO)
this.option.user.id = userInfo.id
this.option.user.name = userInfo.realname
this.getFile()
},
methods: {
getFile() {
getAction('/onlyfile/file/queryById', {
id: this.id }).then(res => {
this.option.url = window._CONFIG['officeUrl'] + '/' + res.result.fileUrl
this.option.editUrl = window._CONFIG['callBackUrl'] + '?userId=' + this.option.user.id + '&fileId=' + this.id
this.option.title = res.result.fileName
this.option.fileType = res.result.fileExt
})
}
},
watch: {
'$route'(to, from) {
this.id = to.params.id
this.getFile()
}
}
}
</script>
<style scoped>
</style>
注意:key 不用设置,会自动分配一个,如果用文件id设置为key,则打开文件时,会用key在文档服务器中寻找文件,已有的key会直接打开,且不可编辑。
window._CONFIG的定义:
//在线文档后端地址
Vue.prototype.OFFICE_API_URL = process.env.VUE_APP_OFFICE_API_URL
window._CONFIG['officeUrl'] = Vue.prototype.OFFICE_API_URL + '/sys/common/static' //上传地址
window._CONFIG['callBackUrl'] = Vue.prototype.OFFICE_API_URL + '/onlyfile/file/editCallBack' //编辑器回调地址
.env文件中
VUE_APP_OFFICE_API_URL=http://192.168.124.200:8080
5、将这个控件配置一个路由地址,就可以使用了
router.config.js中
{
//在线文件编辑
path: '/fileEditor/:id',
component: () => import('@/components/onlyoffice/fView')
},
使用:
goEditor(id){
let routeUrl = this.$router.resolve({
path: "/fileEditor/"+id
});
window.open(routeUrl.href,'_blank')
}
参数id 是文件的id
6、后端回调地址:
/**
* 在线编辑器回调操作
*/
@RequestMapping(path = "/editCallBack", method = {
RequestMethod.POST, RequestMethod.GET})
public void editCallBack(HttpServletRequest request, HttpServletResponse response) throws IOException {
PrintWriter writer = response.getWriter();
Scanner scanner = new Scanner(request.getInputStream()).useDelimiter("\\A");
String body = scanner.hasNext() ? scanner.next() : "";
JSONObject jsonObj = JSONObject.parseObject(body);
String userId = request.getParameter("userId");
String fileId = request.getParameter("fileId");
log.info("在线编辑器回调参数:{}", jsonObj);
if ((Integer) jsonObj.get("status") == 2) {
String downUrl = (String) jsonObj.get("url"); //保存变更后文件
String changeUrl = (String) jsonObj.get("changesurl"); //保存文件的修改记录
String lastsave = (String) jsonObj.get("lastsave");
try {
fileChangeService.saveChange(downUrl, changeUrl, fileId, userId, lastsave);
} catch (Exception e) {
log.info("保存回调文件出错:{}", e.getMessage());
}
}
writer.write("{\"error\":0}");
}
最后需要返回:“error”:0 ,否则编辑器会出现错误。
7、saveChange 方法,下载文档服务器中最新版文件,覆盖到原文件地址,将原文件备份为历史文件,保存修改记录
@Value(value = "${jeecg.path.upload}")
private String uploadpath;
@Override
public void saveChange(String downUrl, String changeUrl, String fileId, String userId, String changeTime) throws IOException {
File entity = fileService.getById(fileId);
String bizPath = "office" + java.io.File.separator + entity.getUserId();
String fullName = entity.getFileUrl().substring(bizPath.length() + 1); //文件全名
String fName = fullName.substring(0, fullName.lastIndexOf(".")); //文件名
String hisPath = bizPath + java.io.File.separator + fName;
String hisFile = hisPath + java.io.File.separator + entity.getFileName() + "_" + System.currentTimeMillis() + "." + entity.getFileExt();
String zipFile = hisFile + ".zip";
java.io.File dirHisPath = new java.io.File(hisPath);
if (!dirHisPath.exists()) {
dirHisPath.mkdir();
}
//复制保存旧文件
java.io.File oldFile = new java.io.File(uploadpath + java.io.File.separator + bizPath + java.io.File.separator + fullName);
java.io.File dstFile = new java.io.File(uploadpath + java.io.File.separator + hisFile);
FileUtil.copy(oldFile, dstFile, true);
try {
URL url = new URL(downUrl);
URL zipUrl = new URL(changeUrl);
try {
//新版文件
HttpURLConnection connection = (HttpURLConnection) url.openConnection();
InputStream inputStream = connection.getInputStream();
FileOutputStream fileOutputStream = new FileOutputStream(oldFile);
int read = 0;
final byte[] bytes = new byte[1024];
while ((read = inputStream.read(bytes)) != -1) {
fileOutputStream.write(bytes, 0, read);
}
fileOutputStream.flush();
fileOutputStream.close();
connection.disconnect();
//变更记录
HttpURLConnection urlConnection = (HttpURLConnection) zipUrl.openConnection();
InputStream inputStream1 = urlConnection.getInputStream();
java.io.File changeFile = new java.io.File(uploadpath + java.io.File.separator + zipFile);
FileOutputStream fileOutputStream1 = new FileOutputStream(changeFile);
int readZip = 0;
byte[] buffer = new byte[1024];
while ((readZip = inputStream1.read(buffer)) != -1) {
fileOutputStream1.write(buffer, 0, readZip);
}
fileOutputStream1.flush();
fileOutputStream1.close();
urlConnection.disconnect();
inputStream.close();
inputStream1.close();
} catch (IOException e) {
throw e;
}
} catch (MalformedURLException e) {
throw e;
}
FileChange fileChange = new FileChange();
fileChange.setUserId(userId);
fileChange.setFileId(fileId);
fileChange.setHisFileUrl(hisFile);
fileChange.setHisChangeUrl(zipFile);
try {
SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'");
simpleDateFormat.setTimeZone(TimeZone.getTimeZone("UTC"));
Date parse =simpleDateFormat.parse(changeTime);
fileChange.setChangeTime(parse);
} catch (ParseException e) {
fileChange.setChangeTime(new Date());
}
this.save(fileChange);
}
很重要的一个给忘了,需要在前端的index.html中,包含进onlyoffice的js地址
<script type="text/javascript" src="http://192.168.124.200:9999/web-apps/apps/api/documents/api.js"></script>
文章浏览阅读357次。1. 给定一个大小超过 100G 的文件, 其中存在 IP 地址, 找到其中出现次数最多的 IP 地址(hash文件切分) 2. 给定100亿个整数, 找到其中只出现一次的整数(位图变形, 用两位来表示次数). 3. 有两个文件, 分别有100亿个query(查询词, 字符串), 只有1G内存, 找到两个文件的交集(hash文件切分 + 布隆过滤器). 4. 给上千个文件, 每个文件大小为1K -..._给定一个大文件100g,其中包含
文章浏览阅读449次。------- android培训、java培训、期待与您交流! ---------- 设计模式:解决某一类问题最行之有效的方法。Java中23种设计模式:单例模式:解决一个类在内存中只存在一个对象。想要保证对象唯一●为了避免其它程序过多建立该类对象,先禁止其他程序建立该对象。●还为了让其他程序可以访问到该类对象,只好在本类中自定义一个对
文章浏览阅读414次。最后配置格式化json,没有绝对标准,主要是要团队统一。第2和第5配合可以实现css代码用颜色区分。_vscode vue 页面编辑器
文章浏览阅读633次。钩子编程(hooking),也称作“挂钩”,是计算机程序设计术语,指通过拦截软件模块间的函数调用、消息传递、事件传递来修改或扩展操作系统、应用程序或其他软件组件的行为的各种技术。处理被拦截的函数调用、事件、消息的代码,被称为钩子(hook)。Hook 是 PyTorch 中一个十分有用的特性。利用它,我们可以不必改变网络输入输出的结构,方便地获取、改变网络中间层变量的值和梯度。这个功能被广泛用..._pytorch hook
文章浏览阅读3.1k次。NSString *urlStr = [data.imgobjectAtIndex:0]; NSURL *imageUrl = [NSURLURLWithString:urlStr]; NSData *imageData = [NSDatadataWithContentsOfURL:imageUrl]; _ios 图片url 如何知道高度
文章浏览阅读1w次。WIN10 升级了SSD硬盘,使用分区助手 将C盘拷贝到SSD硬盘这一步骤网上很多教程,不再细说请看链接http://www.w10zj.com/Win10xy/Win10yh_4851.html有很多电脑无法在BOIS 启动顺序里面是boot manager ,没有SSD硬盘选项本人经过自行分析找到了另外的方法:使用bcdedit修改的方法:1.右键单击桌面右下角的【..._bootmanger里没有固态硬盘
文章浏览阅读3.1k次。root@wangmiao:~/lesson# vim web.croot@wangmiao:~/lesson# vim wrap.hroot@wangmiao:~/lesson# vim wrap.croot@wangmiao:~/lesson# gcc web.c wrap.c -o webweb.c:9:20: fatal error: singnal.h: 没有那个文件或目录 #inclu..._/tmp/ccesx7q9.o:在函数‘main’中:
文章浏览阅读9.1k次。mac下安装php-redis扩展操作系统版本:10.12.5。下载php-redis,地址:https://nodeload.github.com/nicolasff/phpredis/zip/master下载完成得到phpredis-master.zip移动到/usr/local/目录中sudo cp phpredis-master /usr/local/进入/usr/local/目录cd /u_mac php redis.os 下载
文章浏览阅读381次。前言从业这么多年,接触过银行的应用,Apple的应用,eBay的应用和现在阿里的应用,虽然分属于不同的公司,使用了不同的架构,但有一个共同点就是都很复杂。导致复杂性的原因有很多,如果从架构的层面看,主要有两点,一个是架构设计过于复杂,层次太多能把人绕晕。另一个是根本就没架构,ServiceImpl作为上帝类包揽一切,一杆捅到DAO(就简单场景而言,这种Transaction Script也还凑合,..._阿里巴巴sofa框架运行起来后怎么调用
文章浏览阅读1.1k次。有次oracle还把1098端口占用了原文地址:Jboss中服务配置文件jboss-service.xml作者:kingJboss中服务配置文件jboss-service.xml2007-11-22 23:33:31转自网友:JBOSS默认的各种设置文件中通常定义了以下几个端口,1: The ports found in the default configuration ..._jboss-service.xm
文章浏览阅读2.6k次。mpvue 中input框 回车 触发 confirm事件在输入框 按下回车后,保存关键字,并放入本地存储input绑定的confirm事件<input v-model="keyword" @confirm="saveKeyword" @input="keywordSearch" placeholder="请输入关键字" />在data中声明的数据data(){ ..._input confirm事件
文章浏览阅读946次。上篇文章中,我们重点了解了腐蚀和膨胀这两种最基本的形态学操作,而运用这两个基本操作,我们可以实现更高级的形态学变换。 所以,本文的主角是OpenCV中的morphologyEx函数,它利用基本的膨胀和腐蚀技术,来执行更加高级的形态学变换,如开闭运算、形态学梯度、“顶帽”、“黑帽”等等。第二件事,是浅墨想跟大家做一个关于OpenCV系列文章的书写内容和风格的思想汇..._opening operation