vue+onlyOffice+java : 集成在线编辑word并保存
npcxx1025 2024-07-22 10:05:03 阅读 54
1.docker部署onlyOffice
1.1拉取最新版onlyOffice镜像
<code>sudo docker pull onlyoffice/documentserver
1.2运行以下命令运行容器
其中 -v 后的第一部分是挂载自己的linux的哪个目录
# 启动docker容器,默认启动端口为80,可以进行修改
docker run -i -t -d -e TZ="Asia/Shanghai" -p 6831:80 --restart=always \code>
-v /usr/local/docker/document/logs:/var/log/onlyoffice \
-v /usr/local/docker/document/data:/var/www/onlyoffice/Data \
-v /usr/local/docker/document/lib:/var/lib/onlyoffice \
-v /usr/local/docker/document/db:/var/lib/postgresql onlyoffice/documentserver
运行完容器后开始编写代码
2.前端vue代码:
2.1public文文件夹下index.html引入对应的脚本
其中的ip地址和端口改成自己的
<script type='text/javascript' src='http://192.168.59.164:6831/web-apps/apps/api/documents/api.js'></script>code>
2.2 vue组件代码
<!--onlyoffice 编辑器-->
<template>
<div id='vabOnlyOffice'></div>code>
</template>
<script>
export default {
name: 'VabOnlyOffice',
props: {
option: {
type: Object,
default: () => {
return {}
},
},
},
data() {
return {
doctype: '',
docEditor: null,
}
},
beforeDestroy() {
if (this.docEditor !== null) {
this.docEditor.destroyEditor();
this.docEditor = null;
}
},
watch: {
option: {
handler: function(n) {
this.setEditor(n)
this.doctype = this.getFileType(n.fileType)
},
deep: true,
},
},
mounted() {
if (this.option.url) {
this.setEditor(this.option)
}
},
methods: {
async setEditor(option) {
if (this.docEditor !== null) {
this.docEditor.destroyEditor();
this.docEditor = null;
}
this.doctype = this.getFileType(option.fileType)
let config = {
document: {
//后缀
fileType: option.fileType,
key: option.key ||'',
title: option.title,
permissions: {
edit: option.isEdit,//是否可以编辑: 只能查看,传false
print: option.isPrint,
download: false,
// "fillForms": true,//是否可以填写表格,如果将mode参数设置为edit,则填写表单仅对文档编辑器可用。 默认值与edit或review参数的值一致。
// "review": true //跟踪变化
},
url: option.url,
},
documentType: this.doctype,
editorConfig: {
callbackUrl: option.editUrl,//"编辑word后保存时回调的地址,这个api需要自己写了,将编辑后的文件通过这个api保存到自己想要的位置
lang: option.lang,//语言设置
//定制
customization: {
autosave: false,//是否自动保存
chat: false,
comments: false,
help: false,
// "hideRightMenu": false,//定义在第一次加载时是显示还是隐藏右侧菜单。 默认值为false
//是否显示插件
plugins: false,
},
user:{
id:option.user.id,
name:option.user.name
},
mode:option.model?option.model:'edit',
},
width: '100%',
height: '100%',
token:option.token||''
}
// eslint-disable-next-line no-undef,no-unused-vars
this.docEditor = new DocsAPI.DocEditor('vabOnlyOffice', config)
},
getFileType(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
}
},
}
</script>
2.3父组件中开始引用
要记得注册组件
<div class='qualityManual-container'>code>
<el-dialog :visible.sync="openOffice" width="800px" height="800px" append-to-body >code>
<div class='qualityManual-container-office'>code>
<only-office :option='option' />code>
</div>
</el-dialog>
</div>
对应的data中的属性:
openOffice:false,
//参考vabOnlyOffice组件参数配置
option: {
key:"",
url: '',
isEdit: '',
fileType: '',
title: '',
lang: '',
isPrint: '',
user: { id:null,name:''}
},
对应的method中添加方法(这个方法可以在需要的地方引用):
需要注意的是,这里的ip必须这样填(自己天自己的),因为作为docker运行的onlyoffice,需要这样访问本地linux服务器中的服务程序
注:(自己在本地测试时无法通过localhost进行测试运行)
modifyTem(row){
this.getFile(row.id)
},
getFile(id) {
this.openOffice = true
this.show = false
// getAction('/file/selectById', { id: this.id }).then(res => {
this.option.isEdit = true
// https://dsgrcdnblobprod5u3.azureedge.net/catalog-assets/zh-cn/a32a147f-1f69-4fd5-8a39-c25b2a0093e8/tf67429532_wac-987650ec15fa.docx
this.option.lang = 'zh-CN'
this.option.url = 'http://192.168.59.164:8080/notemode/notemodeltypeenum/download/'+id
this.option.title = '笔录模板'
this.option.fileType = 'docx'
this.option.isPrint = false
this.option.user= { id:12,name:'张三'}
this.option.editUrl = "http://192.168.59.164:8080/notemode/notemodeltypeenum/save/"+id
// this,option.key="11111"code>
// })
},
close() {
this.show = false
},
对应的样式
<style >
.qualityManual-container-office {
width: 100%; /* 确保容器宽度充满整个父元素 */
height: 800px; /* 根据需要调整高度 */
overflow: hidden; /* 避免内部内容溢出 */
}
</style>
3.对应的后端代码
1.dowload 方法是作为流进行文件数据的返回
2.save方法是对在线编辑后对于word的保存回调方法,在vue中有对应url回调地址的设置选项
注意:回调的时候,onlyOffice是通过一个地址(url)让你进行去下载。
有一些是测试时的代码,没有删掉,请大家注意甄别。
@GetMapping("/download/{id}")
public void download(@PathVariable("id")Long id, HttpServletRequest request, HttpServletResponse response) throws Exception {
Notemodeltypeenum notemodeltypeenum = notemodeltypeenumService.selectNotemodeltypeenumByID(id);
String filePath = "/a.docx";
int profileIndex = notemodeltypeenum.getContentStr().indexOf("/profile") + "/profile".length();
String subPath = notemodeltypeenum.getContentStr().substring(profileIndex);
filePath = RuoYiConfig.getProfile()+subPath;
//本地文件
// String configPath = RuoYiConfig.getAttachPath();
// filePath = configPath + attachment.getVirtualpath();
File file = new File(filePath);
if (file.exists()) {
// String filename = attachment.getFilename();
response.setContentType("application/octet-stream");
response.setHeader("Content-Disposition", "attachment;filename=" + URLEncoder.encode("a.docx", "utf-8"));code>
response.setCharacterEncoding("utf-8");
response.setContentLength((int) file.length());
byte[] buff = new byte[(int) file.length()];
BufferedInputStream bufferedInputStream = null;
OutputStream outputStream = null;
try {
outputStream = response.getOutputStream();
bufferedInputStream = new BufferedInputStream(new FileInputStream(file));
int i = 0;
while ((i = bufferedInputStream.read(buff)) != -1) {
outputStream.write(buff, 0, i);
outputStream.flush();
}
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
bufferedInputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
@PostMapping("/save/{id}")
public void save(@PathVariable("id")Long id,@RequestParam Map<String, String> map, HttpServletRequest request, HttpServletResponse response) {
Notemodeltypeenum notemodeltypeenum = notemodeltypeenumService.selectNotemodeltypeenumByID(id);
PrintWriter writer = null;
String body = "";
try {
writer = response.getWriter();
Scanner scanner = new Scanner(request.getInputStream());
scanner.useDelimiter("\\A");
body = scanner.hasNext() ? scanner.next() : "";
scanner.close();
} catch (Exception ex) {
writer.write("get request.getInputStream error:" + ex.getMessage());
return;
}
if (body.isEmpty()) {
throw new GlobalException("ONLYOFFICE回调保存请求体未空");
}
JSONObject jsonObj = JSONObject.parseObject(body);
int status = (Integer) jsonObj.get("status");
int saved = 0;
String key = jsonObj.get("key").toString();
if (status == 2 || status == 3 || status == 6) //MustSave, Corrupted
{
String downloadUri = (String) jsonObj.get("url");
System.out.println(downloadUri);
try {
String fileName = generateUniqueFileName(extractFileName(notemodeltypeenum.getContentStr()));
String savePath = RuoYiConfig.getProfile()+"/"+fileName;
downloadFile(downloadUri,savePath);
// 更新原数据的文件地址
String s = replacePathAfterProfile(notemodeltypeenum.getContentStr(), "/" + fileName);
notemodeltypeenum.setContentStr(s);
notemodeltypeenumService.updateNotemodeltypeenum(notemodeltypeenum);
} catch (Exception ex) {
saved = 1;
ex.printStackTrace();
}
}
writer.write("{\"error\":" + saved + "}");
}
public static void downloadFile(String fileUrl, String saveDir) {
try {
URL url = new URL(fileUrl);
URLConnection connection = url.openConnection();
// 5000 milliseconds is an example timeout value
connection.setConnectTimeout(5000);
connection.setReadTimeout(5000);
try (BufferedInputStream in = new BufferedInputStream(connection.getInputStream());
FileOutputStream fileOutputStream = new FileOutputStream(saveDir)) {
byte[] dataBuffer = new byte[1024];
int bytesRead;
while ((bytesRead = in.read(dataBuffer, 0, 1024)) != -1) {
fileOutputStream.write(dataBuffer, 0, bytesRead);
}
}
System.out.println("File downloaded successfully: " + saveDir);
} catch (IOException e) {
System.out.println("Error downloading the file: " + e.getMessage());
e.printStackTrace();
}
}
//
// public static void main(String[] args) {
// downloadFile("https://dsgrcdnblobprod5u3.azureedge.net/catalog-assets/zh-cn/a32a147f-1f69-4fd5-8a39-c25b2a0093e8/tf67429532_wac-987650ec15fa.docx","D:/新建文件夹/87650ec15fa.docx");
// }
public static String generateUniqueFileName(String originalFilename) {
String fileExtension = ""; // 文件扩展名
int i = originalFilename.lastIndexOf('.');
if (i > 0) {
fileExtension = originalFilename.substring(i);
}
return UUID.randomUUID().toString() + fileExtension; // 生成带有扩展名的唯一文件名
}
public static String extractFileName(String url) {
// 检查url是否为空
if (url == null || url.isEmpty()) {
return "";
}
// 查找最后一个斜杠的位置
int lastIndex = url.lastIndexOf('/');
if (lastIndex == -1) {
return ""; // 如果没有找到斜杠,返回空字符串
}
// 从最后一个斜杠之后的位置开始提取子字符串
return url.substring(lastIndex + 1);
}
public static String replacePathAfterProfile(String url, String newPath) {
if (url == null || url.isEmpty()) {
return url; // 或抛出异常,取决于你的错误处理策略
}
// 找到"profile"后的第一个斜杠的索引
int profileIndex = url.indexOf("/profile");
if (profileIndex == -1) {
return url; // 如果找不到"profile",返回原始URL
}
// 计算"profile"之后的部分开始的索引
int startReplaceIndex = profileIndex + "/profile".length();
// 构造新的URL
return url.substring(0, startReplaceIndex) + newPath;
}
参考文章:
onlyoffice+vue实现在线预览在线编辑_onlyoffice vue-CSDN博客
SpringBoot集成onlyoffice实现word文档编辑保存-阿里云开发者社区
声明
本文内容仅代表作者观点,或转载于其他网站,本站不以此文作为商业用途
如有涉及侵权,请联系本站进行删除
转载本站原创文章,请注明来源及作者。