浅谈onlyoffice开发全流程(二、文档加载、回调保存)

1、文件加载流程,引入onlyoffice插件


#前端以为vue为例,在index.html引入插件
<script src="http://localhost/web-apps/apps/api/documents/api.js"></script>
#config 配置
export const documentConfig = {
	"document": {
		"fileType": "docx",
		"isForm": true,
		"info": {
			"favorite": true,
			"folder": "Example Files",
			"owner": "John Smith",
			"sharingSettings": [{
				"permissions": "Full Access",
				"user": "John Smith"
			},
			{
				"isLink": true,
				"permissions": "Read Only",
				"user": "External link"
			}],
			"uploaded": "2010-07-07 3:46 PM"
		},
		"key": "Khirz6zTPdfd7",
		"permissions": {
			"chat": true,
			"comment": false,
			"commentGroups": [{
				"edit": ["Group2", ""],
				"remove": [""],
				"view": ""
			}],
			"copy": true,
			"deleteCommentAuthorOnly": false,
			"download": true,
			"edit": true,
			"editCommentAuthorOnly": false,
			"fillForms": true,
			"modifyContentControl": true,
			"modifyFilter": true,
			"print": true,
			"protect": true,
			"review": true,
			"reviewGroups": ["Group1", "Group2", ""],
			"userInfoGroups": ["Group1", ""]
		},
		"referenceData": {
			"fileKey": "BCFA2CED",
			"instanceId": "https://example.com"
		},
		"title": "Example Document Title.docx",
		"url": "192.168.48.1:3000/劳动合同2.docx"
	},
	"documentType": "word",
	"editorConfig": {
		"actionLink": "ACTION_DATA",
		"callbackUrl": "",
		"coEditing": {
			"mode": "fast",
			"change": true
		},
		"createUrl": "https://example.com/url-to-create-document/",
		"customization": {
			"about": true,
			"anonymous": {
				"request": true,
				"label": "Guest"
			},
			"autosave": true,
			"close": {
				"visible": true,
				"text": "Close file"
			},
			"comments": true,
			"compactHeader": false,
			"compactToolbar": false,
			"compatibleFeatures": false,
			"customer": {
			},
			"features": {
				"featuresTips": true,
				"roles": true,
				"spellcheck": {
					"mode": true,
					"change": true
				},
				"tabBackground": {
					"mode": "header",
					"change": true
				},
				"tabStyle": {
					"mode": "fill",
					"change": true
				}
			},
			"feedback": {
			},
			// "font": {
			// 	"name": "Arial",
			// 	"size": "11px"
			// }, //社区版本无权限
			"forcesave": true,
			"forceWesternFontSize": false,
			"goback": {
			},
			"help": true,
			"hideNotes": false,
			"hideRightMenu": true,
			"hideRulers": false,
			"integrationMode": "embed",
			"layout": {
				"header": {
					"editMode": true,
					"save": true,
					"user": true,
					"users": true
				},
				"leftMenu": {
					"mode": true,
					"navigation": true,
					"spellcheck": true
				},
				"rightMenu": {
					"mode": true
				},
				"statusBar": {
					"actionStatus": true,
					"docLang": true,
					"textLang": true
				},
				"toolbar": {
					"collaboration": {
						"mailmerge": true
					},
					"draw": true,
					"file": {
						"close": true,
						"info": true,
						"save": true,
						"settings": true
					},
					"home": {},
					"layout": true,
					"plugins": true,
					"protect": true,
					"references": true,
					"save": true,
					"view": {
						"navigation": true
					}
				}
			},
			// "loaderLogo": "https://example.com/loader-logo.png", //社区版本无权限
			// "loaderName": "The document is loading, please wait...",//社区版本无权限
			"logo": {
				"image": "https://example.com/logo.png",
				"imageDark": "https://example.com/dark-logo.png",
				"imageLight": "https://example.com/light-logo.png",
				"url": "https://example.com",
				"visible": true
			},
			"macros": false,
			"macrosMode": "warn",
			"mentionShare": true,
			"mobile": {
				"forceView": true,
				"info": false,
				"standardView": false
			},
			"plugins": true,
			"pointerMode": "select",
			// "review": {
			// 	"hideReviewDisplay": false,
			// 	"showReviewChanges": false,
			// 	"reviewDisplay": "original",
			// 	"trackChanges": true,
			// 	"hoverMode": false
			// },
			"showHorizontalScroll": true,
			"showVerticalScroll": true,
			"slidePlayerBackground": "#000000",
			"submitForm": {
				"visible": true,
				"resultMessage": "text"
			},
			"toolbarHideFileName": false,
			"uiTheme": "theme-dark",
			"unit": "cm",
			"wordHeadingsColor": "#00ff00",
			"zoom": 100
		},
		"embedded": {
		},
		"lang": "zh",
		"mode": "edit",
		"plugins": {},
		"recent": [],
		"region": "en-US",
		
		"user": {
		}
	},
	"events": {},
	"height": "100%",
	"token": ",
	"type": "desktop",
	"width": "100%"
}

2、前端加载文件

const initOnlyofficeEditor = () => {
vueOnlyoffceEditor.value =  new window.DocsAPI.DocEditor(tempEleId.value, {
    ...editConfig.value,
    document: {
      ...editConfig.value.document,
      fileType: 'docx',
      title: 'Comparison Mode',
      permissions: {
        ...editConfig.value.document.permissions,
        edit: true,
        download: true
      }
    },
    editorConfig: {
      ...editConfig.value.editorConfig,
      mode: 'edit',
      callbackUrl: 'http://192.168.48.1:8000/service/onlyoffice/callback', // 回调地址
    },
    events: {
      onAppReady: () => {
        console.log('onAppReady: 编辑器初始化完成');
      },
      onDocumentReady: () => {
        console.log('onDocumentReady: 文档加载完成');
      },
      onDocumentStateChange: () => {
        console.log('onDocumentStateChange: 文档状态变更');
      },
      onError: (error: any) => {
        console.error('onError:', error);
      },
      onRequestClose: () => {
        console.log('onRequestClose: 用户请求关闭编辑器');
      },
      onRequestHistory: onRequestHistory,
      onRequestHistoryData:onRequestHistoryData,
      onRequestSaveAs: (event: any) => {
        console.log('onRequestSaveAs:', event);
      },
      onRequestEditRights: () => {
        console.log('onRequestEditRights: 用户请求编辑权限');
      }
    }
  });
}

3、后台回调函数地址,包含获取文件,存储文件,存储文件信息

import express from 'express';
import path from 'path';
import fs from 'fs';
import https from 'https';
import http from 'http';
import { fileURLToPath } from 'url';
import { dirname } from 'path';
import cors from 'cors';
import { v4 as uuidv4 } from 'uuid';


import {saveFileInfo,openDB,getFileListByContractId} from './dbUtil.js';


const __filename = fileURLToPath(import.meta.url);
const __dirname = dirname(__filename);

// 配置 saved_files 目录路径
const SAVED_FILES_DIR = path.join(__dirname, 'saved_files');
const SAVED_ZIP_DIR = path.join(__dirname, 'saved_zip');

const app = express();
const port = 8000;

// 解析 JSON 请求体
app.use(express.json());
app.use(cors({
    origin: '*', // 允许所有来源
    methods: 'GET, POST, PUT, DELETE',
    allowedHeaders: 'Content-Type, Authorization'
}))
// 封装 HTTP 返回函数
const sendResponse = (res, status, msg, data = null) => {
  res.status(status).json({
    status: status,
    msg: msg,
    data: data
  });
}

// 示例路由:返回欢迎消息
app.get('/', (req, res) => {
  sendResponse(res, 200, 'Welcome to the OnlyOffice backend service!');
});
//把下载文件和保存文件部分代码抽出来作为一个函数
function downloadAndSaveFile(fileUrl, changesurl,curKey,status) {
    console.log("status:",status );
    const key = uuidv4();
    if (fileUrl) {
        const filePath = path.join(__dirname, 'saved_files', `${key}.docx`);
        const zipPath = path.join(__dirname, 'saved_zip', `${key}.zip`);
        const file = fs.createWriteStream(filePath);
        const zip = fs.createWriteStream(zipPath);
        const protocol = fileUrl.startsWith('https') ? https : http;
        /**
         * 写入docx文件
         */
        protocol.get(fileUrl, (response) => {
            response.pipe(file);
            file.on('finish', () => {
                file.close();
                // 保存文件信息到 MongoDB
                saveFileInfo(key,curKey)
                    .then(() => {
                        console.log(`File info for ${key,curKey} saved to MongoDB`);
                    })
                    .catch((error) => {
                        console.error('Error saving file info to MongoDB:', error);
                    });
                console.log(`File saved to ${filePath}`);
            });
        }).on('error', (err) => {
            console.error('Error downloading file:', err.message);
        });
        /**
         * 写入zip文件
         */
        protocol.get(changesurl, (response) => {
            response.pipe(zip);
            zip.on('finish', () => {
                zip.close();
                console.log(`Changes file saved to ${zipPath}`);
            });
        }).on('error', (err) => {
            console.error('Error downloading changes file:', err.message);
        });
    }
}
// 回调处理函数
app.post('/service/onlyoffice/callback', (req, res) => {
    const callbackData = req.body;
    const savedDir = path.join(__dirname, 'saved_files');
    if (!fs.existsSync(savedDir)) {
        fs.mkdirSync(savedDir, { recursive: true });
    }
    if (!callbackData || !callbackData.status) {
        return res.status(400).send({ error: 'Invalid callback data' });
    }

    const status = callbackData.status;
    const fileUrl = callbackData.url; // 编辑后的文件 URL
    const curKey = callbackData.key; // 文件唯一标识
    const changesurl = callbackData.changesurl; // 文件变化URL
    const history = callbackData.history; // 文件历史记录
    console.log("callbackData:",callbackData ,status);
    console.log("history:",history);
    switch (status) {
        case 1: // 文档正在编辑中
            console.log(`Document ${curKey} is being edited.${status}`);
            break;
        case 2: // 文档已保存
            console.log(`Document ${curKey} has been saved.${status}`);
            downloadAndSaveFile(fileUrl, changesurl, curKey,status);
            break;
        case 3: // 文档已关闭,未保存
            console.log(`Document ${curKey} was closed without saving${status}.`);
            break;
        case 6: // 强制保存
            downloadAndSaveFile(fileUrl, changesurl,curKey,status);
            console.log(`Document ${curKey} was forcefully saved.${status}`);
            break;
        default:
            console.log(`Unhandled status: ${status}`);
    }

    // 响应 ONLYOFFICE 回调
    res.status(200).send({ error: 0 });
});
/**
 * 获取所有文件信息,根据合同id
 */
app.get('/service/onlyoffice/files/:contractId', (req, res) => {
    const contractId = req.params.contractId; 
    getFileListByContractId(contractId)
        .then((files) => {
            sendResponse(res, 200, 'Files retrieved successfully', files);
        })
        .catch((error) => {
            console.error('Error getting files by contractId:', error);
            sendResponse(res, 500, 'Failed to get files', { error: 'Failed to get files' });
        });
});

/**
 * 根据文件名获取文件内容
 */
app.get('/service/onlyoffice/files/content/:fileName', (req, res) => {
    const fileName = req.params.fileName;
    const filePath = path.join(SAVED_FILES_DIR, `${fileName}.docx`);

    if (!fs.existsSync(filePath)) {
        return sendResponse(res, 404, 'File not found');
    }

    res.sendFile(filePath, (err) => {
        if (err) {
            console.error('Error sending file:', err);
            sendResponse(res, 500, 'Failed to send file');
        }
    });
});
app.get('/service/onlyoffice/files/zip/:fileName', (req, res) => {
    const fileName = req.params.fileName;
    const filePath = path.join(SAVED_ZIP_DIR, `${fileName}.zip`);

    if (!fs.existsSync(filePath)) {
        return sendResponse(res, 404, 'File not found');
    }
    res.sendFile(filePath, (err) => {
        if (err) {
            console.error('Error sending file:', err);
            sendResponse(res, 500, 'Failed to send file');
        }
    });
});

// 启动服务
// 启动服务前连接数据库
openDB()
    .then(() => {
        console.log('Database connected successfully.');
        app.listen(port, () => {
            console.log(`Server is running at http://localhost:${port}`);
        });
    })
    .catch((error) => {
        console.error('Failed to connect to the database:', error);
        process.exit(1); // 终止服务
    });

4、整个开发流程纯前端编写,服务也是,后续会把仓库代码发到git,感兴趣的小伙伴可以下载运行
在这里插入图片描述

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值