Изготовление сайтов в Питере, СПБ. Добавьте функциональные возможности Office в свое веб-приложение с помощью OnlyOffice

 
 

Всякий раз, когда мы пытаемся добавить в приложение какую-либо сложную функциональность, возникает вопрос: «Должен ли я создавать свои собственные?» И если ваша цель не состоит в создании этой функциональности, ответ почти всегда будет прямым «нет».

Что вам нужно, так это что-то, что поможет вам как можно быстрее достичь MVP, и лучший способ добиться этого — использовать готовое решение, которое может помочь вам сэкономить время, что, в свою очередь, переводит в экономии затрат на разработку.

Я предполагаю, что вы все еще здесь, потому что вышеизложенное резонирует с вами. Итак, теперь, когда мы синхронизированы, я хочу показать вам в этой статье, насколько легко интегрировать OnlyOffice в ваше веб-приложение.

Что такое ТолькоОфис?

С их сайта:

OnlyOffice предлагает наиболее многофункциональный офисный пакет, полностью совместимый с форматами файлов Microsoft Office и OpenDocument. Просматривайте, редактируйте и совместно работайте с документами, электронными таблицами и презентациями прямо из вашего веб-приложения.

Офисный пакет имеет несколько редакций. В этой статье мы собираемся использовать версию Developer Edition, потому что мы хотим интегрировать редакторы в приложение, которое впоследствии будет доставлено многим пользователям в виде облачной службы или локальной установки.

Если вы хотите использовать OnlyOffice в рамках существующего решения для синхронизации и совместного использования, вам следует проверить Enterprise Edition. Список интеграций здесь.

Версия для разработчиков

Версия Developer Edition не только дает вам достаточную свободу для интеграции редакторов в ваше приложение, но также включает опцию «White Label», которая позволяет полностью настроить редакторы для использования под вашим собственным брендом.

Интеграция с сервером документов

Для интеграции с вашим веб-приложением сначала необходимо загрузить Документы OnlyOffice (упакованный как Сервер документов) и настроить его на локальном сервере.

После того, как вы его установили, вы можете начать реализовывать запросы для обработки документов на вашем сервере. 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) ;

}) ;

Здесь мы удостоверяемся, что папка резервного копирования для этого файла существует, и возвращаем массив всех файлов резервных копий в этой папке. Да, это поможет вам в вашей задаче хранения большего количества резервных копий для одного файла. Я не могу продолжать делать всю работу за вас!

Открытие документа в браузере

Мы увидим, как мы можем открыть наши документы для редактирования непосредственно в браузере с помощью OnlyOffice Docs.

Открытие документа

Сначала мы создадим простой файл HTML:

<! DOCTYPE html>

Documents

Create docx
Create xlsx
Create pptx

Как видите, в этом файле не так уж много. У нас есть placeholderdiv, к которому будет прикреплен редактор. Затем есть 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маршрут, который мы создали ранее, чтобы получить все файлы, пройтись по ним и создать соответствующие элементы. Мы будем идентифицировать каждый элемент с именем файла в качестве идентификатора. Таким образом, мы можем легко получить их позже.

Обратите внимание, что мы вызываем addDocumentHtmlфункцию, которую позже будем повторно использовать для добавления новых файлов.

Для каждого из этих документов мы также вызываем openDocument, который мы определили внизу, а для символа креста мы вызываем deleteDocument, который мы определим далее.

Удаление документов

Чтобы удалить наши документы, мы сообщим пользователю, уверены ли они, прежде чем мы продолжим, вызовем /deleteмаршрут и начнем ядерный удар по этому файлу. Вместо того, чтобы тратить еще один вызов нашего API, мы проверяем, что возвращаемый статус 200должен удалять элементы DOM напрямую:

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либо text, spreadsheetлибо presentation. Это нужно OnlyOffice, чтобы знать, какой редактор открывать.

Это editDocumentто, для чего мы здесь. Это то, чего вы ждали все это время. Здесь мы создаем экземпляр DocEditorобъекта, передавая идентификатор нашего placeholderdiv и объект с кучей конфигураций.

Конфигурация редактора документов

То, что я показал вам до сих пор, — это минимально необходимые параметры для создания экземпляра DocEditor. Вы должны проверить раздел «Дополнительные параметры «в документации, чтобы увидеть, как вы можете извлечь выгоду из всех различных вариантов. А пока позвольте мне познакомить вас с основами.

Вверху у нас есть поле документа, которое принимает объект, содержащий информацию о документе, который мы хотим открыть.

Затем у нас есть documentType, который, как мы видели ранее, может быть либо text, spreadsheetлибо presentation.

Прямо под ним находится объект editorConfig, который, среди прочего, позволяет вам устанавливать такие вещи, как spellcheck, unitи. zoomВ этом случае мы просто используем callbackUrl, который является URL-адресом /trackмаршрута, который Сервер документов будет использовать для сохранения файла.

Вывод

Мы подошли к концу, и вы, надеюсь, узнали, как настроить и интегрировать Документы OnlyOffice с вашим веб-приложением. Мы многое упускаем, например разрешения, совместное использование, настройку и многие другие вещи, которые вы можете делать с OnlyOffice.

Я надеюсь, что у вас достаточно информации, чтобы продолжать улучшать свой продукт, или, может быть, даже вдохновения, чтобы начать новый проект с нуля. Нет времени лучше настоящего.

Хорошо, увидимся в следующем. А пока продолжайте кодить и не забывайте получать удовольствие, пока вы этим занимаетесь!

3D-печать5GABC-анализAndroidAppleAppStoreAsusCall-центрChatGPTCRMDellDNSDrupalExcelFacebookFMCGGoogleHuaweiInstagramiPhoneLinkedInLinuxMagentoMicrosoftNvidiaOpenCartPlayStationPOS материалPPC-специалистRuTubeSamsungSEO-услугиSMMSnapchatSonyStarlinkTikTokTwitterUbuntuUp-saleViasatVPNWhatsAppWindowsWordPressXiaomiYouTubeZoomАвдеевкаАктивные продажиАкцияАлександровск ЛНРАлмазнаяАлчевскАмвросиевкаАнализ конкурентовАнализ продажАнтимерчандайзингАнтрацитАртемовскАртемовск ЛНРАссортиментная политикаБелгородБелицкоеБелозерскоеБердянскБизнес-идеи (стартапы)БрендБрянкаБукингВахрушевоВендорВидеоВикипедияВирусная рекламаВирусный маркетингВладивостокВнутренние продажиВнутренний маркетингВолгоградВолновахаВоронежГорловкаГорнякГорскоеДебальцевоДебиторкаДебиторская задолженностьДезинтермедитацияДзержинскДивизионная система управленияДизайнДимитровДирект-маркетингДисконтДистрибьюторДистрибьюцияДобропольеДокучаевскДоменДружковкаЕкатеринбургЕнакиевоЖдановкаЗапорожьеЗимогорьеЗолотоеЗоринскЗугрэсИжевскИловайскИрминоКазаньКалининградКировскКировскоеКомсомольскоеКонстантиновкаКонтент-маркетингКонтент-планКопирайтингКраматорскКрасноармейскКрасногоровкаКраснодарКраснодонКраснопартизанскКрасный ЛиманКрасный ЛучКременнаяКураховоКурскЛисичанскЛуганскЛутугиноМакеевкаМариупольМаркетингМаркетинговая информацияМаркетинговые исследованияМаркетинговый каналМаркетинг услугМаркетологМарьинкаМедиаМелекиноМелитопольМенеджментМерчандайзерМерчандайзингМиусинскМолодогвардейскМоскваМоспиноНижний НовгородНиколаевНиколаевкаНишевой маркетингНовоазовскНовогродовкаНоводружескНовосибирскНумерическая дистрибьюцияОдессаОмскОтдел маркетингаПартизанский маркетингПервомайскПеревальскПетровскоеПлата за кликПоисковая оптимизацияПопаснаяПравило ПаретоПривольеПрогнозирование продажПродвижение сайтов в ДонецкеПроизводство видеоПромоПромоушнПрямой маркетингРабота для маркетологаРабота для студентаРазработка приложенийРаспродажаРегиональные продажиРекламаРеклама на асфальтеРемаркетингРетро-бонусРибейтРитейлРовенькиРодинскоеРостов-на-ДонуРубежноеСамараСанкт-ПетербургСаратовСватовоСвердловскСветлодарскСвятогорскСевастопольСеверодонецкСеверскСедовоСейлз промоушнСелидовоСимферопольСинергияСколковоСлавянскСнежноеСоздание сайтов в ДонецкеСоледарСоциальные сетиСочиСтаробельскСтаробешевоСтахановСтимулирование сбытаСуходольскСчастьеТелемаркетингТельмановоТираспольТорговый представительТорезТрейд маркетингТрейд промоушнТюменьУглегорскУгледарУкраинскХабаровскХарцызскХерсонХостингЦелевая аудиторияЦифровой маркетингЧасов ЯрЧелябинскШахтерскЮжно-СахалинскЮнокоммунаровскЯндексЯсиноватая