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文档编辑保存-阿里云开发者社区 



声明

本文内容仅代表作者观点,或转载于其他网站,本站不以此文作为商业用途
如有涉及侵权,请联系本站进行删除
转载本站原创文章,请注明来源及作者。