每当我们发现自己要为一个应用程序添加任何复杂的功能时,就会产生这样的问题:"我应该自己开发吗?"除非你的目标是建立这种功能,否则答案几乎总是直接的 "不"。
你需要的是帮助你尽快获得MVP的东西,而实现这一目标的最好方法是使用一个完整的开箱即用的解决方案,它可以帮助你节省时间,而这又可以转化为节省开发成本。
我假设你仍然在这里,因为上述内容与你产生了共鸣。所以,现在我们已经同步了,我想在这篇文章中向你展示的是,将Onlyoffice整合到你的网络应用中是多么容易。
什么是Onlyoffice?
从他们的网站。
ONLYOFFICE提供了功能最丰富的办公套件,与Microsoft Office和OpenDocument文件格式高度兼容。直接从您的Web应用程序中查看、编辑和协作处理文档、电子表格和演示文稿。
在这种情况下,我们将使用开发版,因为它对我们的目的来说是最好的,但如果你想与其他服务(如SharePoint)集成,那么你应该检查一下集成版。
开发者版
开发者版不仅给你足够的自由在你的应用程序中整合编辑器,而且它还带有一个 "白标签 "选项,让你完全定制编辑器,在你自己的品牌下使用它们。
文档服务器集成
为了与你的网络应用集成,你首先需要下载Document Server安装,并在你的本地服务器上设置它。
另一个选择是使用下面的命令用Docker来安装它。
docker run -i -t -d -p 8080:80 onlyoffice/documentserver
在你安装了它之后,你可以开始实现在你的服务器上处理文档的请求。Onlyoffice为.NET、Java、Node.js、PHP、Python和Ruby提供了一些非常好的例子。
你可以下载文档服务器和你喜欢的例子,并在你的机器上直接尝试。
我将演示你如何开始整合到你的应用程序中。为此,我们将使用一个非常简单的Node.js和Express的例子。我不会在实现上做太多的细节,我将列出最基本的内容,让你来填补空白,以建立一个强大的、可扩展的系统。
我有一个具有以下结构的应用程序。
- node_modules
- public
- backups
- css
- main.css
- documents
- sample.docx
- javascript
- main.js
- samples
- new.docx
- new.xlsx
- new.pptx
- app.js
- index.html
- package.json
我们将使用public/documents 文件夹来存储文件。app.js 文件是我们的Express应用代码所在的地方,而index.html 是我们要显示文件的地方。为了测试,我把一个sample.docx 文件放在文档文件夹里。
public/samples/ 里面的树状文件是空白文件,我们将在 "创建 "新文件时复制它。
backups 文件夹,你将在后面看到,不仅可以帮助我们保留以前版本的备份,还可以帮助我们在修改文件后为其生成唯一的标识符。
public/css/main.css 和public/javascript/main.js 文件将被index.html 使用。我们将在后面研究这个问题。
让我们看一下app.js 文件。
const express = require('express');
const bodyParser = require("body-parser");
const path = require('path');
const fs = require('fs');
const syncRequest = require('sync-request');
const app = express();
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: false }));
app.use(express.static("public"));
app.get("/", (req, res) => {
res.sendFile(path.join(__dirname, "/index.html"));
});
const port = process.env.PORT || 3000;
app.listen(port, () => console.log(`App listening on http://localhost:${port}`));
我们正在做的是将文件作为localhost:3000/documents/filename 。
我还超越了自己,添加了syncRequest 、fs 和bodyParser 。这些现在并不相关,但我们以后会用到它们。
获取文件
为了显示可用的文件,我们需要获得一个所有文件名的列表,并将它们发送给客户端。我们将为此创建/documents 路线。
app.get("/documents", (req, res) => {
const docsPath = path.join(__dirname, "public/documents");
const docsPaths = fs.readdirSync(docsPath);
const fileNames = [];
docsPaths.forEach(filePath => {
const fileName = path.basename(filePath);
fileNames.push(fileName);
});
res.send(fileNames);
});
创建文件
一开始我们只有一个样本文件,但这一点都不好玩。让我们添加一个/create 路由来协助我们添加一些文件。我们将简单地取一个fileName ,并将相应的模板复制到public/documents 文件夹中,并为其取新的名字。
app.post("/create", async (req, res) => {
const ext = path.extname(req.query.fileName);
const fileName = req.query.fileName;
const samplePath = path.join(__dirname, "public/samples", "new" + ext);
const newFilePath = path.join(__dirname, "public/documents", fileName);
// Copy the sample file to the documents folder with its new name.
try {
fs.copyFileSync(samplePath, newFilePath);
res.sendStatus(200);
} catch (e) {
res.sendStatus(400);
}
});
删除文件
我们还需要一种方法来删除文件。让我们创建一个/delete 路由。
app.delete("/delete", (req, res) => {
const fileName = req.query.fileName;
const filePath = path.join(__dirname, "public/documents", fileName);
try {
fs.unlinkSync(filePath);
res.sendStatus(200);
} catch (e) {
res.sendStatus(400);
}
});
这个是超级简单的。我们将删除文件并发送一个200 状态代码,让用户知道一切都很顺利。否则,他们会得到一个400 状态代码。
保存文件
到目前为止,我们可以打开我们的文件进行编辑,但我们没有办法保存我们的修改。现在让我们来做这件事。我们将添加一个/track 路线来保存我们的文件。
app.post("/track", async (req, res) => {
const fileName = req.query.fileName;
const backupFile = filePath => {
const time = new Date().getTime();
const ext = path.extname(filePath);
const backupFolder = path.join(__dirname, "public/backups", fileName + "-history");
// Create the backups folder if it doesn't exist
!fs.existsSync(backupFolder) && fs.mkdirSync(backupFolder);
// Remove previous backup if any
const previousBackup = fs.readdirSync(backupFolder)[0];
previousBackup && fs.unlinkSync(path.join(backupFolder, previousBackup));
const backupPath = path.join(backupFolder, time + ext);
fs.copyFileSync(filePath, backupPath);
}
const updateFile = async (response, body, path) => {
if (body.status == 2) {
backupFile(path);
const file = syncRequest("GET", body.url);
fs.writeFileSync(path, file.getBody());
}
response.write("{\"error\":0}");
response.end();
}
const readbody = (request, response, path) => {
const content = "";
request.on("data", function (data) {
content += data;
});
request.on("end", function () {
const body = JSON.parse(content);
updateFile(response, body, path);
});
}
if (req.body.hasOwnProperty("status")) {
const filePath = path.join(__dirname, "public/documents", fileName);
updateFile(res, req.body, filePath);
} else {
readbody(req, res, filePath);
}
});
这是一个棘手的问题,因为当编辑器保存文件时,它将被文档服务器使用。正如你所看到的,我们正在返回"{\"error\":0}" ,这告诉服务器一切都很好。
当编辑器关闭时,文件的当前版本将被备份在public/backups/fileName-history/ ,文件的名称是当前的时间(以毫秒为单位)。我们以后会在前端使用该文件的名称,你会看到。
在这个例子中,我们每次保存一个新的备份时,都会替换掉之前的备份。你会如何去保存更多的备份呢?
取出备份
我们需要一种方法来获取特定文件的备份,所以我们要添加一个/backups 路线来处理这个问题。
app.get("/backups", (req, res) => {
const fileName = req.query.fileName;
const backupsPath = path.join(__dirname, "public/backups", fileName + "-history");
if (!fs.existsSync(backupsPath)) {
return res.send([]);
}
const backupsPaths = fs.readdirSync(backupsPath);
const fileNames = [];
backupsPaths.forEach(filePath => {
const fileName = path.basename(filePath);
fileNames.push(fileName);
});
res.send(fileNames);
});
这里我们要确保该文件的备份文件夹存在,并返回该文件夹中所有备份文件的数组。是的,这将帮助你完成为单个文件保留更多备份的任务。我不能一直为你做所有的工作!
