PDF Tech

How to Save Files to Your Server Using Foxit PDF SDK for Web

by PDF SDK | December 19, 2022

The ability to save files such as images, videos, and PDFs to a server is a common convenience across industries. As a developer, you’ll need to prioritize this feature when building applications.

The following are some use cases in which it’s necessary to save files to a server:

* Invoices: Businesses and individuals increasingly use invoicing platforms to generate and share invoices for clients.

* E-commerce: Receipts and other financial documents are stored for both customers and companies as part of the online shopping process.

* Large companies: As companies grow, they require more accounting files, resumes, and other paperwork that they need to store for future reference.

In this tutorial, you’re going to save files to your server using the Foxit PDF SDK for Web. Saving files to a storage server enables you to retrieve, send, or transfer them as needed. You’ll be using an object storage with Amazon Web Services to upload a file, then download that file to the server.

To follow along, check this repository.

Prerequisites

You’ll need the following for this tutorial:

* Some knowledge of JavaScript, HTML, CSS, and Node.js

* The latest version of Node.js installed on your machine

* Foxit PDF SDK for Web (JavaScript) installed on your machine (download a free trial if you haven’t already)

* An AWS account

Saving Files Using the Foxit PDF SDK for Web

The Foxit PDF SDK for Web is a lightweight, powerful PDF library for web applications that works with Foxit’s core rendering engine. You’re going to build a simple UI using HTML and CSS to upload a test PDF file to object storage, then retrieve the file from storage. In this project, when you choose a file to upload, its content will also be rendered to the screen.

First, integrate the SDK into a new project. Create a new project folder or directory and name it FoxitPDF. Go into the extracted SDK directory and copy or create the following folders and files:

* external

* lib

* server

* app.js

* license-key.js (found inside the examples folder)

* package.json

Create new index.html and app.js files. Open your terminal and run npm install to install all dependencies.

Copy the following code into your HTML file to create the UI:

html
<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width,initial-scale=1">
    <title>Foxit PDF SDK For Web</title>
    <link rel="shortcut icon" href="../../assets/favicon.ico" type="image/x-icon" />
    <link rel="stylesheet" href="../../../lib/PDFViewCtrl.css" />
</head>
<style>
    @import url("https://fonts.googleapis.com/css2?family=Poppins:wght@200;300;400;500;600;700&display=swap");
    @import url('https://fonts.googleapis.com/css2?family=Noto+Sans:ital,wght@0,400;0,700;1,400;1,700&display=swap');

    * {
        margin: 0;
        padding: 0;
        box-sizing: border-box;
        font-family: "Poppins", sans-serif;
    }

    .container {
        height: 100%;
        width: 100%;
        align-items: center;
        display: flex;
        justify-content: center;
        background-color: #fcfcfc;
    }

    .card {
        border-radius: 10px;
        box-shadow: 0 5px 10px 0 rgba(0, 0, 0, 0.3);
        width: 600px;
        height: 360px;
        background-color: #ffffff;
        padding: 10px 30px 40px;
        margin-top: 20px;
    }

    .card h3 {
        font-size: 22px;
        font-weight: 600;

    }

    .drop_box {
        margin: 10px 0;
        padding: 30px;
        display: flex;
        align-items: center;
        justify-content: center;
        flex-direction: column;
        border: 3px dotted #a3a3a3;
        border-radius: 5px;
    }

    .drop_box h4 {
        font-size: 16px;
        font-weight: 400;
        color: #2e2e2e;
    }

    .drop_box p {
        margin-top: 10px;
        margin-bottom: 20px;
        font-size: 12px;
        color: #a3a3a3;
    }

    .btn {
        text-decoration: none;
        background-color: #005af0;
        color: #ffffff;
        padding: 10px 20px;
        border: none;
        outline: none;
        transition: 0.3s;
    }

    .btn-input {
        text-decoration: none;
        background-color: #fff;
        color: #000;
        border: 1px solid #005af0;
        padding: 10px 20px;
        outline: none;
        transition: 0.3s;
        margin-bottom: 20px;
    }

    .btn-input:hover {
        cursor: pointer;
    }

    .btn:hover {
        text-decoration: none;
        background-color: #ffffff;
        color: #005af0;
        padding: 10px 20px;
        border: none;
        outline: 1px solid #010101;
        cursor: pointer;
    }

    .form input {
        margin: 10px 0;
        width: 100%;
        background-color: #e2e2e2;
        border: none;
        outline: none;
        padding: 12px 20px;
        border-radius: 4px;
    }
</style>

<body>
    <div class="container">
        <div class="card">
            <h3>Upload Files</h3>
            <div class="drop_box">
                <header>
                    <h4>Select File here</h4>
                </header>
                <p>Files Supported: PDF, TEXT, DOC, DOCX</p>
                <form method="post" enctype="multipart/form-data" id="form">
                    <input type="file" name="file" id="file" class="btn-input" accept=".pdf,.fdf,.xfdf" multiple="multiple">
                    <button class="btn">Submit</button>

                </form>
                    <button class="btn" id="download-btn">Download</button>
            </div>

        </div>
    </div>
    <button type="button" class="btn" id="plus">Zoom In</button>
    <button type="button" class="btn" id="sub">Zoom Out</button>
    <hr>
    <div style="width: 100%; height: 500px; overflow: auto;">
        <div id="pdf-viewer"></div>
    </div>

    <script src="license-key.js"></script>
    <script src="/lib/UIExtension.full.js"></script>
    <script src="/lib/PDFViewCtrl.full.js"></script>
    <script src="/lib/PDFViewCtrl/addon/EditGraphicsAddonModule.js"></script>
    <script src="/lib/PDFViewCtrl/addon/CreateAnnotAddonModule.js"></script>
    <script src="/lib/PDFViewCtrl/addon/HContinuousViewMode.js"></script>
    <script src="/lib/PDFViewCtrl/addon/FacingContinuousViewMode.js"></script>
    <script src="/lib/PDFViewCtrl/addon/FacingPageViewMode.js"></script>

    <script src="./app.js"></script>
</body>

</html>

For illustrative purposes, the styling is also in the HTML file, but you can move it to a separate file and import it however you want.

To check what you’ve done so far, run npm start. This will bundle up the SDK and your page to serve it on your localhost, which is on port 8080.

You should see this in your terminal:

This shows the default path hosted by the server. To fix that, open server/index.js and remove the following code block:

js
   if(localAddress) {
       console.log(boxen(`
${chalk.bold('Basic WebViewer Address')}: ${chalk.cyan(localAddress+'examples/PDFViewCtrl/basic_webViewer/')}
 
${chalk.bold('Complete WebViewer Address')}: ${chalk.cyan(localAddress+'examples/UIExtension/complete_webViewer/')}
       `, {
           borderColor: 'green',
           borderStyle: 'bold',
           padding: 1,
           margin: 1
       }));
   }

Add the following code block in its place:

js
if (localAddress) {
       console.log(boxen(`${chalk.bold('File upload site is live at')}: ${chalk.cyan(`${localAddress}index.html`)}`, {
           borderColor: 'green',
           borderStyle: 'bold',
           padding: 1,
           margin: 1
       }));
   }

Stop the server and run it again with npm start. This will change the log path in your terminal to the one you just created. You should see the following result:

Click the link to open it in your browser. Your rendered UI should look like this:

Next, implement the Foxit SDK into the project to upload PDF files. Import PDFViewCtrl, the View controller that contains other methods and classes for rendering, viewing, and controlling the engines of the Foxit SDK.

Copy the following code to app.js:

js
const pdf = PDFViewCtrl;
const PDFViewer = PDFViewCtrl.PDFViewer;
const PDFUi = UIExtension;

This imports PDFViewCtrl and extracts the PDFViewer class from it, which is used to control the viewing properties of the SDK. The PDFUi class is also extracted.

Next, instantiate the PDFViewer class:

js
const pdfViewer = new PDFViewer({
  libPath: "./lib",
  jr: {
    licenseSN: licenseSN,
    licenseKey: licenseKey,
  },
  customs: {
    // Custom function to confirm the removal of documents
    closeDocBefore: function () {
      return confirm("Close the current document?");
    },
    // Custom function to render pages
    PageCustomRender: (function () {
      function CustomPageCustomRender(eCustom, pdfPageRender) {
        this.eCustom = eCustom;
        this.pdfPageRender = pdfPageRender;
      }
      // Custom function to limit the amount of pages to view
      CustomPageCustomRender.prototype.render = function () {
        let self = this;
        return self.pdfPageRender.getPDFPage().then(function (page) {
          if (page.getIndex() > 3) {
            self.eCustom.innerHTML =
              "You are not authorized to view this page.";
            return false;
          }
        });
      };
      // Custom function to remove the render whenever you change the file in the file input
      CustomPageCustomRender.prototype.destroy = function () {
        this.eCustom.innerHTML = "";
      };
      return CustomPageCustomRender;
    })(),
    ScrollWrap: PDFViewCtrl.CustomScrollWrap,
  },
});

This code uses the following configurations:

* libPath is the path to the lib folder

* licenseSN is the variable licenseSN from the license-key.js file

* licenseKey is the variable licenseKey from the license-key.js file

Additionally, customs is an object of custom functions. The ones here are for rendering the uploaded document so you can see it in real time, confirming before a document is closed, limiting the number of pages you can scroll through, and scrolling through the rendered document.

To instantiate the PDF viewer with the DOM (adding it to the HTML node), add this code block:

js
// init the pdf host
pdfViewer.init("#pdf-viewer");

Next, test the file input. Add the following code block:

js
let fileName = undefined;
document.getElementById("file").onchange = function (e) {
  if (!e.target.value) {
    return;
  }

  let pdf, fdf;
  for (let i = e.target.files.length; i--; ) {
    let file = e.target.files[i];
    let filename = file.name;
    fileName = file.name;
    if (/\.pdf$/i.test(filename)) {
      pdf = file;
      console.log(pdf, "po");
    } else if (/\.(x)?fdf$/i.test(filename)) {
      fdf = file;
    }
  }
}

This checks the file to see if it’s a PDF file.

Downloading and Uploading Files Using getStream()

Paste the following code block into the previous function handling events for file input:

js
  // send file functionality
  let file = undefined;
  function loadFileAndDownload(pdfDoc) {
    let bufferArray = [];
    return pdfDoc
      .getStream(function ({ arrayBuffer, offset, size }) {
        bufferArray.push(arrayBuffer);
      })
      .then(function (size) {
        file = new Blob([JSON.stringify(bufferArray)], {
          type: "application/pdf",
        });
        return new Blob(bufferArray, { type: "application/pdf" });
      });
  }

  // The pdfViewer method (openPDFByFile) returns a type of PDFDoc, this is how you can use the getStream() method.
  let file_uploaded = pdfViewer.openPDFByFile(pdf, {
    password: "",
    fdf: { file: fdf },
  });
  file_uploaded.then((res) => {
    loadFileAndDownload(res);
  });

The openPDFByFile() method returns a type PDFDoc, which is used to open the local document. This type is used for PDF operations like downloading and uploading. Check the Foxit resources for more information.

The loadFileAndDownload() function takes the file as an argument and creates a buffer by using getStream(), which is available to the type PDFDoc. getStream() takes an arrayBuffer, size, and offset, then returns a promise. This is a new blob containing the file, which is now a buffer, and other configurations.

Next, you’ll move from the frontend to the backend server.

Saving Files to a Server

To test out this file conversion and send it to a server, you’ll need to build a server. You’ll need to implement two routes in your backend project and connect it with any cloud object storage service.

This tutorial uses AWS, so you’ll need an AWS account created.

AWS offers free tiers for the S3 cloud object storage, so you don’t need to pay anything to continue in this tutorial.

Next, you need to grant public read access to your bucket. You need to do this because you are going to view and download the uploaded file. Once you’ve done that, you can make a POST request containing the file and a GET request to download the file.

In your project root, create a new folder named file_server, cd into that directory, and run npm init. Answer the questions and run npm install express multer cors multer-s3 dotenv aws-sdk.

Create a new file in that folder named server.js to be your server index. Copy the following code into the file:

js
require("dotenv").config();
const express = require("express");
const app = express();
const port = 3000;
const cors = require("cors");
const aws = require("aws-sdk");
const multer = require("multer");
const multerS3 = require("multer-s3");

app.use(
  cors({
    origin: "*",
  })
);

The code above imports all the modules needed and uses the CORS method into the app (more on this later). You’re using `multer` to accept fields from forms. It also has aws-sdk S3 integration.

Create a route, copy the code block, and test your server by running npm start after adding the following code to server.js:

js
app.get("/", (req, res) => {
  res.send("Hello World!");
});
 
app.listen(port, () => {
  console.log(`Example app listening on port ${port}`);
});

Create an .env file to keep your secret key and access key copied from your AWS S3 bucket. Copy the code block below into your server.js file and add the variables from your .env file:

js
const s3 = new aws.S3({
  secretAccessKey: process.env.SECRET,
  accessKeyId: process.env.ACCESS,
  region: YOUR_REGION
});

Your complete server.js file should look like this:

js
require("dotenv").config();
const express = require("express");
const app = express();
const port = 3000;
const cors = require("cors");
const aws = require("aws-sdk");
const multer = require("multer");
const multerS3 = require("multer-s3");

app.use(
  cors({
    origin: "*",
  })
);
const s3 = new aws.S3({
  secretAccessKey: process.env.SECRET,
  accessKeyId: process.env.ACCESS,
  region: YOUR_REGION
});

// Change bucket property to your Space name
const upload = multer({
  storage: multerS3({
    s3: s3,
    bucket: "web-uploads",
    acl: "public-read",
    contentType: multerS3.AUTO_CONTENT_TYPE,
    key: function (request, file, cb) {
      // console.log(file, "dile");
      cb(null, file.originalname);
    },
  }),
}).any();
// }).array("file", 1);

app.get("/", (req, res) => {
  res.send("Hello World!");
});


let corsOptions = {
  origin: [
    "https://NAME_OF_BUCKET.s3.REGION.amazonaws.com/",
    "http://localhost:8080/",
  ],
};

app.get("/download", cors(corsOptions), function (req, res) {
  var fileName = req.query.filename;
  console.log(req.body, 'li');
  var directory =
    "https://NAME_OF_BUCKET.s3.REGION.amazonaws.com/” + fileName;

  console.log(directory, 'yay');
  res.json({
    data: directory
  })
});

app.post("/upload", function (request, response, next) {
  upload(request, response, function (error) {
    if (error) {
      console.log(error, "error");
      return response.send("Error, something went wrong");
    }
    if (!request) {
      console.log("no request found");
    }
    console.log(request.body, "File uploaded successfully.");
    response.send("Success, file uploaded");
  });
});

app.listen(port, () => {
  console.log(`Example app listening on port ${port}`);
});

This contains all the logic for sending files using Multer with its configurations, downloading files, and the CORS config, which is needed for downloading the file.

Notice this line in the CORS configuration:  “https://NAME_OF_BUCKET.s3.REGION.amazonaws.com/”. This is how to access your bucket’s URL.

If you remove the cors configuration at the download route, it’ll give you an error. For more details about CORS, check this resource from MDN Web Docs.

This completes the backend.

Uploading to and Downloading from the Server

Go back to the frontend. Copy the following code for uploading the file to the server and paste it into the function in app.js handling the input file:

js
  // logic to submit when user presses submit or enter
  document.getElementById("form").onsubmit = async function (e) {
    e.preventDefault();
    const body = new FormData(document.getElementById("form"));
    file_uploaded.then((res) => {
      loadFileAndDownload(res).then((res) => {
        console.log(res, "dance");
        body.append("file", res);
      });
    });

    // request to send file
    await fetch("http://localhost:3000/upload", {
      method: "POST",
      body: body,
    })
      .then(() => {
        alert("The file has been uploaded successfully.");
      })
      .catch((err) => {
        alert("Oh no, something went wrong!");
      });
  };

The following code is for the download feature. Paste it outside the function handling the input file:

js
document
  .getElementById("download-btn")
  .addEventListener("click", async function (e) {
    e.preventDefault();
    console.log("clicked");
    await fetch(`http://localhost:3000/download?filename=${fileName}`, {
      method: "GET",
    })
      .then((res) => {
        res.json().then((res) => {
          downloadURI(res.data, fileName);
        });
      })
      .catch((err) => {
        alert("Oh no, something went wrong");
      });
  });

// Logic to download file
function downloadURI(uri, name) {
  var link = document.createElement("a");
  link.download = name;
  link.href = uri;
  window.open(uri, "_blank");
}

To test this out, try uploading a file. Once you see the alert saying the file has been uploaded successfully, you should be able to download the file by clicking the Download button. Make sure your backend server and your frontend server are running, using npm start for both sides.

Now for something fun. It’s time to use the Zoom In and Zoom Out buttons. Copy the code below into your frontend code and restart the frontend server:

js
// Zoom in and Zoom out
let scale = 1;
document.getElementById("plus").onclick = function () {
  scale += 0.25;
  pdfViewer.zoomTo(scale).catch(function () {});
};
document.getElementById("sub").onclick = function () {
  scale -= 0.25;
  pdfViewer.zoomTo(scale).catch(function () {});
};

This allows you to zoom the file rendered to the browser in and out.

Your complete app.js file should look like this:

js
const pdf = PDFViewCtrl;
const PDFViewer = PDFViewCtrl.PDFViewer;
const PDFUi = UIExtension;

const pdfViewer = new PDFViewer({
  libPath: "./lib",
  jr: {
    licenseSN: licenseSN,
    licenseKey: licenseKey,

  },
  customs: {
    // Custom function to confirm the removal of documents
    closeDocBefore: function () {
      return confirm("Close the current document?");
    },
    // Custom function to render pages
    PageCustomRender: (function () {
      function CustomPageCustomRender(eCustom, pdfPageRender) {
        this.eCustom = eCustom;
        this.pdfPageRender = pdfPageRender;
      }
      // Custom function to limit the amount of pages to view
      CustomPageCustomRender.prototype.render = function () {
        let self = this;
        return self.pdfPageRender.getPDFPage().then(function (page) {
          if (page.getIndex() > 3) {
            self.eCustom.innerHTML =
              "You are not authorized to view this page.";
            return false;
          }
        });
      };
      // Custom function to remove the render whenever you change the file in the file input
      CustomPageCustomRender.prototype.destroy = function () {
        this.eCustom.innerHTML = "";
      };
      return CustomPageCustomRender;
    })(),
    ScrollWrap: PDFViewCtrl.CustomScrollWrap,
  },
});

// init the pdf host
pdfViewer.init("#pdf-viewer");

let fileName = undefined;
document.getElementById("file").onchange = function (e) {
  if (!e.target.value) {
    return;
  }

  let pdf, fdf;
  for (let i = e.target.files.length; i--; ) {
    let file = e.target.files[i];
    let filename = file.name;
    fileName = file.name;
    if (/\.pdf$/i.test(filename)) {
      pdf = file;
      console.log(pdf, "po");
    } else if (/\.(x)?fdf$/i.test(filename)) {
      fdf = file;
    }
  }

  // send file functionality
  let file = undefined;
  function loadFileAndDownload(pdfDoc) {
    let bufferArray = [];
    return pdfDoc
      .getStream(function ({ arrayBuffer, offset, size }) {
        bufferArray.push(arrayBuffer);
      })
      .then(function (size) {
        file = new Blob([JSON.stringify(bufferArray)], {
          type: "application/pdf",
        });
        return new Blob(bufferArray, { type: "application/pdf" });
      });
  }

  // The pdfViewer method (openPDFByFile) returns a type of PDFDoc, this is how you can use the getStream() method.
  let file_uploaded = pdfViewer.openPDFByFile(pdf, {
    password: "",
    fdf: { file: fdf },
  });
  file_uploaded.then((res) => {
    loadFileAndDownload(res);
  });

  // logic to submit when user presses submit or enter
  document.getElementById("form").onsubmit = async function (e) {
    e.preventDefault();
    const body = new FormData(document.getElementById("form"));
    file_uploaded.then((res) => {
      loadFileAndDownload(res).then((res) => {
        console.log(res, "dance");
        body.append("file", res);
      });
    });

    // request to send file
    await fetch("http://localhost:3000/upload", {
      method: "POST",
      body: body,
    })
      .then(() => {
        alert("The file has been uploaded successfully.");
      })
      .catch((err) => {
        alert("Oh no, something went wrong!");
      });
  };
};

document
  .getElementById("download-btn")
  .addEventListener("click", async function (e) {
    e.preventDefault();
    console.log("clicked");
    await fetch(`http://localhost:3000/download?filename=${fileName}`, {
      method: "GET",
    })
      .then((res) => {
        res.json().then((res) => {
          downloadURI(res.data, fileName);
        });
      })
      .catch((err) => {
        alert("Oh no, something went wrong");
      });
  });

// Logic to download file
function downloadURI(uri, name) {
  var link = document.createElement("a");
  link.download = name;
  link.href = uri;
  window.open(uri, "_blank");
}

// Zoom in and Zoom out
let scale = 1;
document.getElementById("plus").onclick = function () {
  scale += 0.25;
  pdfViewer.zoomTo(scale).catch(function () {});
};
document.getElementById("sub").onclick = function () {
  scale -= 0.25;
  pdfViewer.zoomTo(scale).catch(function () {});
};

This is how it should work if all goes well:

How to save files to a server using foxitWeb demo (tutorial)

Conclusion

In this tutorial, you learned about the Foxit PDF SDK for Web, worked with some of its class objects, integrated them with your project, and rendered a PDF file into the browser. You also built the backend for uploading and downloading the file using cloud object storage.

Foxit is a leading software provider of solutions for reading, editing, creating, organizing, and securing PDF documents. The Foxit PDF SDK for Web is a cross-platform solution for PDF online viewing, and its enterprise edition is used by many of the world’s leading firms. Check the documentation for more information.

Consult the repository to check your work on this tutorial.

Author: Ayomide Bajo