飞码网-免费源码博客分享网站

点击这里给我发消息

使用OnlyOffice为您的Web应用程序添加Office功能|-JavaScript教程

飞码网-免费源码博客分享网站 爱上飞码网—https://www.codefrees.com— 飞码网-matlab-python-C++ 爱上飞码网—https://www.codefrees.com— 飞码网-免费源码博客分享网站

每当我们发现自己试图向应用程序添加任何复杂的功能时,就会出现问题:“我应该自己滚动吗?” 除非您的目标是构建该功能,否则答案几乎总是直截了当的“否”。

您需要的是可以帮助您尽快获得MVP的东西,而实现此目标的最佳方法是使用完整的现成的解决方案,该解决方案可以帮助您节省时间,进而可以节省时间。节省开发成本。

什么是OnlyOffice?

从他们的网站:

OnlyOffice提供了功能最丰富的办公套件,与Microsoft Office和OpenDocument文件格式高度兼容。直接从您的Web应用程序中查看,编辑文档,电子表格和演示文稿并与之协同工作。

办公套件有多个版本。在本文中,我们将使用Developer Edition,因为我们希望将编辑器集成到应用程序中,然后将其作为云服务或本地安装交付给许多用户。

如果要在现有的同步和共享解决方案中使用OnlyOffice,则应签出Enterprise Edition。集成列表在这里。

开发人员版

开发人员版不仅为您提供了将编辑器集成到您的应用程序中的足够自由,而且还提供了“白标”选项,可让您完全自定义编辑器以在您自己的品牌下使用它们。

文件服务器集成

要与您的Web应用程序集成,首先需要下载OnlyOffice Docs(打包为文档服务器)并在本地服务器上进行设置。

安装后,您可以开始实施处理服务器上文档的请求。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.csspublic/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

我也得到了超前了,并添加syncRequestfsbodyParser这些现在不相关,但我们稍后会使用。

提取文件

为了显示可用的文档,我们需要获取所有文件名的列表并将其发送给客户端。我们将/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);
  }
});

这是一个棘手的问题,因为当编辑器保存文件时,它将由Document Server使用。如您所见,我们返回"{\"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);
});

在这里,我们确保该文件的备份文件夹存在,并返回该文件夹中所有备份文件的数组。是的,这将帮助您完成为单个文件保留更多备份的任务。我不能继续为你做所有的工作!

在浏览器中打开文档

我们将看到如何打开文档以使用OnlyOffice Docs在浏览器中直接进行编辑。

打开文件

首先,我们将创建一个简单的HTML文件:

<!DOCTYPE html>
<html>

<head>
  <title>OnlyOffice Example</title>

  <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=0"/>
  <link rel="stylesheet" href="/public/css/main.css">
</head>

<body>
  <div ></div>
  <div >
    <h1>Documents</h1>
    <div >
      <div onclick="createDocument('.docx')">Create docx</div>
      <div onclick="createDocument('.xlsx')">Create xlsx</div>
      <div onclick="createDocument('.pptx')">Create pptx</div>
    </div>
  </div>
  <script type="text/javascript" src="http://localhost:8080/web-apps/apps/api/documents/api.js"></script>
  <script type="text/javascript" src="/public/javascript/main.js"></script>
</body>

</html>

如您所见,该文件没有太多内容。我们有placeholder将要附加编辑器div。然后是documentsdiv,其中包含用于创建文档的控件和用于文件名列表的容器。

在此之下,我们具有带文档服务器JavaScript API的脚本。请记住,您可能必须用文档服务器的位置替换主机。如果使用Docker我给您命令安装了它,那么您应该一切顺利。

最后但并非最不重要的一点是,有一个script标记,我们在其中导入前端JavaScript,还有一个main.js文件,在这里我们可以全局访问该DocsAPI对象。

CSS

在进行编码之前,让我们用一些CSS封装一下布局,以使我们的应用程序更易用,更丑陋。将以下内容添加到main.css

html,
body {
  font-family: monospace;
  height: 100%;
  margin: 0;
  background-color: lavender;
  color: aliceblue;
}

h1 {
  color: lightslategray;
  display: inline-block;
}

#placeholder {
  height: 100%;
}

#documents {
  text-align: center;
}

#document-controls {
  text-align: center;
  margin: 5px;
}

#document-controls>div {
  display: inline-block;
  font-size: 15px;
  cursor: pointer;
  padding: 10px;
  background: mediumaquamarine;
}

#documents-list {
  padding: 5px;
  max-width: 400px;
  margin: auto;
}

.document {
  cursor: pointer;
  font-size: 20px;
  text-align: left;
  padding: 5px;
  margin: 2px;
  background-color: lightsteelblue;
}

.delete-doc {
  color: lightslategray;
  float: right;
  margin: 0 5px 0 5px;
}

显示可用文件

有了这些,我们就可以开始对前端进行编码了。我们将从列出文件documents中的文件开始。转到main.js并添加以下代码:

const params = new URLSearchParams(window.location.search);
const fileName = params.get("fileName");

if (fileName) {
  editDocument(fileName);
} else {
  listDocuments();
}

function listDocuments() {
  // Hide the editor placeholder
  document.getElementById("placeholder").style.display = "none";
  // Remove old list
  const oldList = document.getElementById("documents-list");
  oldList && oldList.remove();
  // Create new container
  const documentsHtml = document.getElementById("documents");
  const docsListHtml = document.createElement("div");
  docsListHtml.id = "documents-list";

  documentsHtml.appendChild(docsListHtml);

  const req = new XMLHttpRequest();

  req.addEventListener("load", function (evt) {
    const docs = JSON.parse(this.response);

    docs.forEach(doc => {
      addDocumentHtml(doc);
    });
  });

  req.open("GET", "/documents");
  req.send();
}

function addDocumentHtml(fileName) {
  const docsListHtml = document.getElementById("documents-list");

  const docElement = document.createElement("div");
  docElement.id = fileName;
  docElement.textContent = fileName;
  docElement.setAttribute("class", "document");

  docElement.onclick = () => {
    openDocument(fileName);
  }

  const deleteElement = document.createElement("span");
  deleteElement.textContent = "X";
  deleteElement.setAttribute("class", "delete-doc");

  deleteElement.onclick = evt => {
    evt.stopPropagation();
    evt.preventDefault();
    deleteDocument(fileName);
  }

  docElement.appendChild(deleteElement);
  docsListHtml.appendChild(docElement);
}

function openDocument(fileName) {
  const url = "/?fileName=" + fileName;
  open(url, "_blank");
}

在顶部,我们获取查询参数以了解是否正在打开文件。如果是,我们将调用该editDocument函数。不用担心,我们稍后会创建一个。

 

 

如果我们不打开文件,则要显示可用文件的列表以及创建更多文件的控件。在中listDocuments,我们首先确保隐藏placeholder并清除列表以确保重新创建它。然后,我们调用/documents先前创建路由来获取所有文件,遍历它们,并创建相应的元素。我们将使用文件名作为ID来标识每个元素。这样,我们以后可以轻松检索它们。

注意,我们正在调用该addDocumentHtml函数,稍后将重复使用函数以添加新文件。

对于这些文档中的每一个,我们还调用了openDocument我们在底部定义的,而在十字符号上则调用了deleteDocument我们接下来定义的。

删除文件

要删除我们的文档,我们会提示用户是否确定,然后再继续调用该/delete文件并对其进行核查。我们正在检查返回的状态是否是200直接删除DOM元素,而不是浪费对API的另一个调用

function deleteDocument(fileName) {
  const canContinue = confirm("Are you sure you want to delete " + fileName + "?");

  if (!canContinue) {
    return;
  }

  const req = new XMLHttpRequest();

  req.addEventListener("load", function (evt) {
    if (this.status === 200) {
      return removeDocumentHtml(fileName);
    }

    alert("Could not delete " + fileName);
  });

  req.open("DELETE", "/delete?fileName=" + fileName);
  req.send();
}

function removeDocumentHtml(fileName) {
  const el = document.getElementById(fileName);
  el && el.remove();
}

建立文件

还记得我们onclick在文档创建控件中调用的函数吗?干得好:

function createDocument(extension) {
  const name = prompt("What's the name of your new document?");
  const fileName = name + "." + extension;

  const req = new XMLHttpRequest();

  req.addEventListener("load", function (evt) {
    if (this.status === 200) {
      addDocumentHtml(fileName);
      return;
    }

    alert("Could not create " + fileName);
  });

  req.open("POST", "/create?fileName=" + fileName);
  req.send();
}

非常简单。我们提示输入名称,以该名称/create作为fileName参数调用路由,如果状态返回,200则调用addDocumentHtml来直接添加DOM元素。

在OnlyOffice Docs中打开文档

现在我们需要定义editDocument函数。将以下代码添加到main.js

async function editDocument(fileName) {
  document.getElementById("documents").style.display = "none";

  const extension = fileName.substring(fileName.lastIndexOf(".") + 1);
  const documentType = getDocumentType(extension);
  const documentKey = await generateKey(fileName);

  console.log(documentKey);

  new DocsAPI.DocEditor("placeholder", {
    document: {
      fileType: extension,
      key: documentKey,
      title: fileName,
      url: "http://192.168.0.7:3000/documents/" + fileName,
    },
    documentType,
    editorConfig: {
      callbackUrl: "http://192.168.0.7:3000/track?fileName=" + fileName,
    },
    height: "100%",
    width: "100%",
  });
}

function generateKey(fileName) {
  return new Promise(resolve => {
    const req = new XMLHttpRequest();

    req.addEventListener("load", function (evt) {
      const backups = JSON.parse(this.response);
      const backupName = backups[0];
      const key = backupName ? backupName.substring(0, backupName.indexOf(".")) : new Date().getTime();
      resolve(String(key));
    });

    req.open("GET", "/backups?fileName=" + fileName);
    req.send();
  });
}

function getDocumentType(extension) {
  const documentTypes = {
    text: ["doc", "docx", "docm", "dot", "dotx", "dotm", "odt", "fodt", "ott", "rtf", "txt", "html", "htm", "mht", "pdf", "djvu", "fb2", "epub", "xps"],
    spreadsheet: ["xls", "xlsx", "xlsm", "xlt", "xltx", "xltm", "ods", "fods", "ots", "csv"],
    presentation: ["pps", "ppsx", "ppsm", "ppt", "pptx", "pptm", "pot", "potx", "potm", "odp", "fodp", "otp"],
  }

  if (documentTypes.text.indexOf(extension) >= 0) {
    return "text";
  }
  if (documentTypes.spreadsheet.indexOf(extension) >= 0) {
    return "spreadsheet";
  }
  if (documentTypes.presentation.indexOf(extension) >= 0) {
    return "presentation";
  }
}

因此,我们添加了三个功能。让我们先关注最后两个。(我们稍后再谈editDocument。)

generateKey也将产生密钥帮助我们。这是用于服务识别文档的唯一文档标识符。它的最大长度为20,并且没有特殊字符。这就是窍门:每次保存文档时都必须重新生成它。你知道这是怎么回事吗?究竟!我们将从备份文件名中获利来生成密钥。

如您所见,要生成密钥,我们将检索唯一的备份(如果有),并使用其名称;如果没有备份,则以毫秒为单位简单地获取当前时间。

如果要支持更多备份,该功能将有什么变化?[逃跑]

getDocumentType将返回textspreadsheetpresentationOnlyOffice需要此来知道要打开哪个编辑器。

editDocument就是我们在这里的目的。这就是您一直在等待的东西。在这里,我们实例化DocEditor传递placeholderdiv ID的对象和具有一堆配置的对象。

DocEditor配置

到目前为止,我向您展示的是实例化的最低要求选项DocEditor您应该查看文档中的“高级参数”部分,以了解如何从所有不同的选项中受益。同时,让我为您介绍基本知识。

在顶部,我们有一个document字段,它包含一个对象,该对象包含有关我们要打开的文档的信息。

然后我们有documentType,正如我们前面所看到的,可以是textspreadsheetpresentation

在它的正下方是editorConfig对象,该对象可让您设置spellcheckunitzoom等。在这种情况下,我们仅使用callbackUrl,即/track文档服务器用来保存文件路由的URL

结论

我们已经到了尽头,希望您已经学会了如何设置OnlyOffice Docs并将其与Web应用程序集成。我们遗漏了很多东西,例如权限,共享,自定义以及OnlyOffice可以执行的许多其他操作。

我希望您有足够的信息来继续改进产品,或者甚至是从头开始新项目的灵感。没有像现在这样的时间。

 

飞码网-免费源码博客分享网站 爱上飞码网—https://www.codefrees.com— 飞码网-matlab-python-C++ 爱上飞码网—https://www.codefrees.com— 飞码网-免费源码博客分享网站
赞 ()
内容页底部广告位3
留言与评论(共有 0 条评论)
   
验证码: