Build an online document converter with ONLYOFFICE Conversion API

10 February 2023By Serge

Document conversion is a very popular and valuable feature that helps us operate with multiple document types. ONLYOFFICE Editors can easily convert documents to a wide range of formats. This blog post will show you how to build an online converter that runs on ONLYOFFICE Conversion API.

Build an online converter with ONLYOFFICE Conversion API

About ONLYOFFICE Conversion API

Document Conversion service allows converting all kinds of Office documents: texts, sheets, slides, forms, PDFs, and eBooks. It facilitates creating PDFs from documents, and sheets, converting textbooks into eBooks, presentations into image files, and more. ONLYOFFICE supports more than 50 different document types.

How it works

Our conversion API operates through the document conversion service. It serves as a part of the ONLYOFFICE Document Server and allows us to convert various document files into the appropriate formats.

The conversion is performed in several steps:

  1. The user selects a file that is to be uploaded to the document manager.
  2. The document manager uploads the selected file to the document storage service.
  3. The document storage service sends the uploaded file to the document conversion service by using the conversion API.
  4. The document conversion service converts the selected file to the targeted format.
  5. The document storage service downloads the converted document file.

Build an online converter with ONLYOFFICE Conversion API

Document manager and document storage services are client and server side tools that facilitate selecting and storing a document for further conversion. However, our web application will tackle those tasks since we are building a custom converter.

Prerequisites

Our converter will be NodeJS based. So for this project, we will need the following:

– Express

– Axios

– Jsonwebtoken

We will use the Axios package to send a post request to the ONLYOFFICE Document Server and the Jsonwebtoken package to sign a JWT token. Starting from version 7.2, JWT authentication is enabled by default.

Alternatively, there is an option to run the ONLYOFFICE Document Server image with JWT authentication disabled. To do that, execute the following command in the terminal:

sudo docker run -i -t -d -p 80:80 -e JWT_ENABLED=false onlyoffice/documentserver

The post request that our converter is sending to ONLYOFFICE Document Server is the following:

{
        "async": true,
        "filetype": fileType,
        "key": key, 
        "outputtype": outputType,
        "title": `Converted Document.${outputType}`,
        "url": link      
    }
  • In the accept parameter, we specify that we want to receive a response in JSON format.
  • The async parameter indicates that the request is asynchronous.
  • The fileType parameter specifies the type of the original file that we are going to convert.
  • The key parameter represents the UID identifier of the current document.
  • The outputType parameter indicates the format of the converted document.
  • The title parameter contains the name of the converted document.
  • The url parameter includes a link to the file that we want to convert.

The values of the fileType, otputType, and url parameters are fetched from the client side of our application and stored in the variables. The value of the key parameter will be randomly generated and stored in the variable as well.

Project set up

After installing the required packages, we go to the app.js file and initialize them alongside the bodyParser to process the post request data. We also set up a public folder and a view engine:

const express = require('express');
const app = express();
const axios = require('axios'); 
const jwt = require('jsonwebtoken');


app.use(express.urlencoded({extended: true}));
app.use(express.json());
app.use(express.static("public"));
app.set('view engine', 'ejs');

Then we add routes. Our app will include a get route and a post route.  They will allow us to fetch the input data and pass it into the post request to ONLYOFFICE Document Server:

app.get ('/', function (reg, response){ 
}

app.post ('/converter', function(req, response){
}

Client-side

Now we take a little detour and look into the client side of our converter. That’s where we will input all the required data:

Build an online converter with ONLYOFFICE Conversion API

Build an online converter with ONLYOFFICE Conversion API

The client-side includes two EJS pages::

  • homepage.ejs – allows us to fetch all the values required for the post request.
  • converter.ejs – allows us to download the converted file.

Let’s take a closer look at the homepage.ejs. Here we created a form that sends data to the post route. First, we get a URL to the original file. We will store it in the link variable on the server side:

<h1 class="h3 mb-3 font-weight-normal">Convert your OOXML files here!</h1>
        <input type="text"  class="form-control" id="inputEmail" placeholder="Paste a link to the file" name="link" onchange="activateBox()">

Then we pick a type of the original file in the combo box. Later on, we will fetch this value and store it in the inputType variable:

<select class="form-control" id="inputType" input type="text" disabled="true" name="inputType" onchange="comboBox()">
          <option value="" selected>Input File Type</option>
          <option value="docx">docx</option>
          <option value="txt">txt</option>
          <option value="pdf">pdf</option>
          <option value="rtf">rtf</option>
          <option value="xml">xml</option>
          <option value="csv">csv</option>
          <option value="xlsx">xlsx</option>
          <option value="xls">xls</option>
          <option value="ppt">ppt</option>
          <option value="pptx">pptx</option>
</select>

Then we choose the desired file type. This value will be stored in the outputType variable on the server side:

<select class="form-control" id="outputType" input type="text" disabled="true" name="outputType" onchange="activateButton()">
      <option value="" disabled selected hidden>Output File Type</option>
    </select>

And we use a button to send the entire data to the converter post route:

<div class="button">
      <button type="submit" id="download" disabled="true" class="btn btn-lg btn-primary btn-block">Convert</button>
    </div>
    </form>

 Building a converter

The fetched data will be parsed on the server side of our application. So now, let’s go to the app.js file and get it:

app.post ('/converter', function(req, response){
    let link = req.body.link;
    let outputType = req.body.outputType;
    let fileType = req.body.inputType;

});

Now let’s take a look at our second combo box that sends the outputType value to the post route:

  <select class="form-control" id="outputType" input type="text" disabled="true" name="outputType" onchange="activateButton()">
      <option value="" disabled selected hidden>Output File Type</option>
    </select>

The outputType variable is included in the post request to the server. It specifies the format of the converted file. Let`s examine the JavaScript code that allows us to interact with the page elements and incorporate a dynamic list into our UI.

The document conversion service is a very powerful tool, and it’s capable of converting various types of files. So our goal is to utilize a dynamic list that will allow us to select a format for the converted file. This list will display all the options available for the particular type of original file.

To do that, we create a JSON file that stores values in the accordance with each particular type:

Build an online converter with ONLYOFFICE Conversion API

Note! For demonstration purposes, we included only the most common OOXML formats. To learn all the supported conversion options, visit our documentation page.

Then we add a function that will generate this dynamic list:

 function comboBox () {
     let type = document.querySelector("#inputType").value;
     let listDropDown = document.querySelector("#outputType");
     fetch("formats.json")
    .then(function(response){
      return response.json();
    })
    .then(function(data){
     let options = data[type];
     let out = "";
     out += `<option value="">Output File Type</option>`;
     Object.values(options).map(function(format){
        out += '<option value=' + format + '>' + format + '</option>';
      });
     listDropDown.innerHTML = out;
     listDropDown.disabled = false;
               });  
    };
        };

First, this function gets the value of the inputType combo box, where we selected the format of the original file. We assign it to the type variable to use this value down the line. Then we make an AJAX request to load the JSON data. After that, we use the value of the type variable as an index and iterate through the loaded data to insert its values into the options of the drop-down list.

Now, every time we select the type of the original file, the script executes and shows us the available conversion options in the accordance with the original file format.

And after we have all the required data, we send it to the converter route by clicking the Convert button.

But that’s not it.  To make our app more interactive, we will add functions that will activate the combo box and button elements in the right sequence:

 function activateBox() {
      $("#inputType").prop('disabled', false)
    };
    
    function activateButton() {
      $("#download").prop('disabled', false)
      $("#outputTypeForm").hide();
    };

The entire JavaScript code is the following:

     function comboBox () {
     let type = document.querySelector("#inputType").value;
     let listDropDown = document.querySelector("#outputType");
     fetch("formats.json")
    .then(function(response){
      return response.json();
    })
    .then(function(data){
     let options = data[type];
     let out = "";
     out += `<option value="">Output File Type</option>`;
     Object.values(options).map(function(format){
        out += '<option value=' + format + '>' + format + '</option>';
      });
     listDropDown.innerHTML = out;
     listDropDown.disabled = false;
               });  
    };
    function activateBox() {
      $("#inputType").prop('disabled', false)
    };
    
    function activateButton() {
      $("#download").prop('disabled', false)
      $("#outputTypeForm").hide();
    };
        }

Let’s take a look at what’s going on on the server side:

app.post ('/converter', function(req, response){
    let link = req.body.link;
    let outputType = req.body.outputType;
    let fileType = req.body.inputType;
    let key =  function () {
        var key = '';
        var str = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' + 
                'abcdefghijklmnopqrstuvwxyz0123456789';
          
        for (let i = 1; i <= 12; i++) {
            var char = Math.floor(Math.random()
                        * str.length + 1);
              
            key += str.charAt(char);
        }
          
        return key;
    };
    const payload =  { 
        "async": true,
        "filetype": fileType,
        "key": key, 
        "outputtype": outputType,
        "title": `Converted Document.${outputType}`,
        "url": link       
    }
    let token = jwt.sign(payload, secret, options);
    
    axios.post( 
        'http://127.0.0.1:83/ConvertService.ashx',
        { 
           "token": token
        }) 
        .then((res) => response.render('converter.ejs', {
            link: res.data.fileUrl
        })) 
});

In the converter route, we formed our post request to the ONLYOFFICE Document Server and stored it in the payload variable. We used fileType, outputType, and link variables which store the data that we fetched. However, we also have the key variable that contains the unique identifier of the current document. So we added a little function above to generate it:

    let key =  function () {
        var key = '';
        var str = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' + 
                'abcdefghijklmnopqrstuvwxyz0123456789';
          
        for (let i = 1; i <= 12; i++) {
            var char = Math.floor(Math.random()
                        * str.length + 1);
              
            key += str.charAt(char)
        }   
        return key;
    };

Now that we have all the necessary values for our request, we use jwt.sign to wrap them in a token:

  let token = jwt.sign(payload, secret, options);

The jwt.sign method takes three parameters:

  • Payload that includes all the parameters required for the successful conversion.
  • Header that contains information on the cyphering algorithm and the expiry time frame. We wrapped these parameters in the options variable.
  • Secret that represents a secret key generated by ONLYOFFICE Document Server. This secret can be found in the local.json file, or by executing the following command in the terminal:
sudo docker exec <dockerID> /var/www/onlyoffice/documentserver/npm/json -f /etc/onlyoffice/documentserver/local.json 'services.CoAuthoring.secret.session.string'

After we sign the token, we use an axios post request to send it to the server. Then we render the converter.ejs page which receives the response from ONLYOFFICE Document Server:

 axios.post( 
        'http://127.0.0.1:83/ConvertService.ashx',
        { 
           "token": token
        }) 
        .then((res) => response.render('converter.ejs', {
            link: res.data.fileUrl
        }))

Here is a sample of the response in JSON format:

{     "endConvert": true,     "fileType": "docx",     "fileUrl": "https://documentserver/url-to-converted-document.pdf",     "percent": 100 }

What need here is the fileUrl element. It is a link to the converted file. So we access and send it to the converter.ejs page:

.then((res) => response.render('converter.ejs', {
            link: res.data.fileUrl
        })) 
});

On that page, we created two buttons. The Go back button takes us back to the homepage.ejs page and the Download button opens the link that we sent to the page and downloads the converted file:

Build an online converter with ONLYOFFICE Conversion API

Note!  To learn more about JWT, visit our documentation page.

The entire server-side code is the following:

const express = require('express');
const app = express();
const axios = require('axios'); 
const jwt = require('jsonwebtoken');
const options = {algorithm: "HS256", expiresIn: "5m"};
const secret = "k1gWQdmDX6ZGiWw5r3g2";
app.use(express.urlencoded({extended: true}));
app.use(express.json());
app.use(express.static("public"));
app.set('view engine', 'ejs');
app.get ('/', function (reg, response){ 
    response.render('homepage.ejs', {
    })
});
app.post ('/converter', function(req, response){
    let link = req.body.link;
    let outputType = req.body.outputType;
    let fileType = req.body.inputType;
    let key =  function () {
        var key = '';
        var str = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' + 
                'abcdefghijklmnopqrstuvwxyz0123456789';
          
        for (let i = 1; i <= 12; i++) {
            var char = Math.floor(Math.random()
                        * str.length + 1);
              
            key += str.charAt(char);
        }
          
        return key;
    };
    const payload =  { 
        "async": true,
        "filetype": fileType,
        "key": key, 
        "outputtype": outputType,
        "title": `Converted Document.${outputType}`,
        "url": link       
    }
    let token = jwt.sign(payload, secret, options);
    
    axios.post( 
        'http://127.0.0.1:83/ConvertService.ashx',
        { 
           "token": token
        }) 
        .then((res) => response.render('converter.ejs', {
            link: res.data.fileUrl
        })) 
});
app.listen(3000,() => console.log('the server is up and running'));

And now, let’s run our converter and see how it works!

ONLYOFFICE solutions are extremely multifunctional. They provide users and developers with a unique experience and allow them to manipulate ooXML documents in various ways. We hope that you will find the information mentioned above useful and apply it in your future projects. Don’t hesitate to leave comments, ask questions or share your ideas with us. We are open to suggestions and cooperation. Best of luck in your exploratory endeavors!