Introduction
We may need to switch formats whenever we need to deliver image content to our users, depending on business requirements and the user’s device configurations. This is especially true for image/PDF formats. In this article, we review how we can easily achieve this using Cloudinary and Nuxt.Js.
The complete project can be found on the CodeSandbox, you can fork it and add the following environment variables to your sandbox server to make it work.
1NUXT_ENV_CLOUDINARY_CLOUD_NAME=2CLOUDINARY_API_KEY=3CLOUDINARY_API_SECRET=
CodeSandbox
The complete codebase can also be found on Github
Prerequisites
We will be using beginner-level Nuxt.Js and Express.Js in this tutorial. Thus we recommend previous knowledge and experience in JavaScript and Vue.Js.
Node.Js, Express.Js, and Nuxt.Js knowledge and experience are not a requirement.
Installation
Nuxt.Js Project
Ensure Node.Js is installed on your system. Npx also needs to be installed as well. It is shipped by default since npm 5.2.0, npm v6.1, or yarn.
Run the following command to create the project
1yarn create nuxt-app nuxtjs-image-pdf-converter
We recommend installing axios
during the prompt as we will use it to interact with the Express.Js server.
Since we will have both client and server-side operations, we recommend selecting the Universal (SSR/SSG)
rendering mode and Server (Node.Js hosting)
deployment mode.
Here is a compilation of our recommended setup:
Project Name: nuxtjs-image-pdf-converter
Programming Language -> JavaScript
Package manager -> Yarn
UI Framework -> TailwindCSS
Nuxt.Js modules -> Axios
Linting tools -> None
Rendering mode -> Universal (SSR/SSG)
Deployment target -> Server (Node.Js hosting)
Development tools -> None
Continuous integration -> None
Version control -> Git
Once the above setup is complete, Enter the project folder with the following command:
1cd nuxtjs-image-pdf-converter
Cloudinary Nuxt
Cloudinary doesn't come installed by default in Nuxt.Js, thus we need to install the recommended package using the following command
1yarn add @nuxtjs/cloudinary23# OR45npm install @nuxtjs/cloudinary
Once the dependency has been added to our project, let's add @nuxt/cloudinary
as a module in our nuxt.config.js
modules section. This registers the package in our Nuxt.Js project.
1// nuxt.config.js23export default {45modules: [67'@nuxtjs/cloudinary'89]1011}
To configure our Cloudinary instance, we'll add a cloudinary
section to our nuxt.config.js
1// nuxt.config.js23cloudinary: {45cloudName: process.env.NUXT_ENV_CLOUDINARY_CLOUD_NAME,67secure: true89}
We do not want our cloud name exposed in our project code. For this reason, we'll create a .env
file. By default, this file is ignored by git
and will not be committed to our remote code repository.
1// .env23NUXT_ENV_CLOUDINARY_CLOUD_NAME=my-cloudinary-cloud-name
Express Server
Nuxt.Js allows us to add our own custom middleware. We are going to use this to create an Express.Js API without creating our own dedicated external server.
We are first going to install Express.Js
1yarn add express23# OR45npm install express
We will create a file server-middleware/api.js
to handle our API requests:
1// server-middleware/api.js23const app = require('express')()45app.all('/generate-pdf', async (req, res) => {67res.json({ data:'data' })89})1011module.exports = app
The above code enables our API to handle all api/generate-pdf
requests.
To register our API with Nuxt.Js we will need to register it in the serverMiddleware
section of nuxt.config.js
1// nuxt.config.js23serverMiddleware: [45{ path: "/api", handler: "~/server-middleware/api.js" },67],
This will ensure that all /api
requests are routed to our API.
Node.Js Cloudinary
To interact with Cloudinary from our Express.Js server, we'll need to install the Node.Js SDK as well. We'll use the following command to install it:
1yarn add cloudinary23# OR45npm install cloudinary
Once installed, we'll need to load and configure the SDK in our api.js
1// server-middleware/api.js23require('dotenv').config()45const cloudinary = require('cloudinary');67cloudinary.config({89cloud_name: process.env.NUXT_ENV_CLOUDINARY_CLOUD_NAME,1011api_key: process.env.CLOUDINARY_API_KEY,1213api_secret: process.env.CLOUDINARY_API_SECRET,1415secure: true1617});1819...
We do not want our API Key and Secret exposed to our code repository. We will thus add them to our .env
file
1// .env23...45CLOUDINARY_API_KEY=secret-api-key67CLOUDINARY_API_SECRET=secret-api-secret
Converting images to PDF
Let us render a simple form allowing our users to upload images.
1<!-- pages/images-to-pdf.vue -->23<form @submit.prevent="submit">45<input67multiple89accept=".jpeg,.jpg,.png,image/jpeg,image/png"1011type="file"1213name="images"1415/>1617<button type="submit">1819Convert2021</button>2223</form>
To convert images to PDF, we'll first upload them to cloudinary. During the upload process, we'll assign them a unique tag. We will then use this tag to generate the PDF.
We'll use a Promise.all to ensure all the images are uploaded. Once the images are uploaded, we then call the api/generate-pdf
endpoint with the new tag as a query parameter. This request should return an instance object containing the secure_url
to our PDF document.
1// pages/images-to-pdf.vue23export default {45data() {67return {89pdfUrl: null1011};1213},1415methods: {1617async submit(e) {1819const randomTag = Math.random().toString(36).substring(7);2021this.uploading = true;2223const files = e.target.images.files;2425const uploadedFiles = await Promise.all(2627Array.from(files).map(async (file) => {2829/* create a reader */3031const readData = (f) =>3233new Promise((resolve) => {3435const reader = new FileReader();3637reader.onloadend = () => resolve(reader.result);3839reader.readAsDataURL(f);4041});424344/* Read data */4546const data = await readData(file);474849/* upload the converted data */5051return await this.$cloudinary.upload(data, {5253upload_preset: "nuxtjs-image-pdf-converter",5455folder: `nuxtjs-image-pdf-converter/images/${randomTag}`,5657tags: [randomTag],5859});6061})6263);6465const resp = await this.$axios.get(`/api/generate-pdf?tag=${randomTag}`);66676869this.pdfUrl = resp.data.pdf.secure_url;70717273this.uploading = false;7475},7677},7879};
Within our api.js
, we will call on the cloudinary multi method. This is the method we will use to create a single PDF from all the uploaded images.
To do this, we will update the configuration we created for the route /generate.pdf
. This is the code contained in the app.all(..)
method we created earlier.
1//server-middleware/api.js23app.all('/generate-pdf', async (req, res) => {45const pdf = await cloudinary.v2.uploader.multi(67req.query.tag,89{ format: "pdf" },1011);12131415res.json({ pdf: pdf })1617})
We will call the method requesting cloudinary to create a new pdf
. We will then send back the response to our front-end.
We will then render a link on the front-end allowing our users to view the newly created PDF.
1<!-- pages/images-to-pdf.vue -->23<a45:href="pdfUrl"67target="_blank"89>1011View PDF1213</a>
Converting PDFs to Images
We will first ensure that only PDF file types can be uploaded by our users. This can be done using simple HTML
1<!-- pages/pdf-to-images.vue -->23<form @submit.prevent="submit">45<input67type="file"89accept="application/pdf"1011name="pdf"1213/>1415<button>Convert</button>1617</form>
On upload, we will upload the PDF document to cloudinary. We will receive an object containing the public_id
and the pages
in the document on upload. We will then use this data to generate URLs rendering each page as an image.
To achieve this, let us add the following code to the section of our pages/pdf-to-images.vue
file. You can find the whole file on github here.
1// pages/pdf-to-images.vue23export default {45data() {67return {89images: null,1011};1213},1415methods: {1617async submit(e) {1819this.uploading = true;2021const file = e.target.pdf.files[0];2223/* create a reader */2425const readData = (f) =>2627new Promise((resolve) => {2829const reader = new FileReader();3031reader.onloadend = () => resolve(reader.result);3233reader.readAsDataURL(f);3435});3637/* Read data */3839const data = await readData(file);4041/* upload the converted data */4243const upload = await this.$cloudinary.upload(data, {4445upload_preset: "nuxtjs-image-pdf-converter",4647folder: `nuxtjs-image-pdf-converter/PDFs`,4849});5051this.images = [];5253for (let page = 1; page <= upload.pages; page++) {5455this.images.push({5657page,5859url: this.$cloudinary.image.url(upload.public_id, {6061page,6263}),6465});6667}6869this.uploading = false;7071},7273},7475};
Once the images
array has been filled with the links, we can then render them for our users to view:
1<!-- pages/pdf-to-images.vue -->23<ul>45<li v-for="(image, index) in images" :key="index">67<a89:href="image.url"1011target="_blank"1213>1415Page {{ image.page }}1617</a>1819</li>2021</ul>
Running the project
To run the project locally, use the following command in the nuxtjs-image-pdf-converter
project folder:
1yarn dev23# OR45npm run dev
Conclusion
In the above article, we have reviewed how to convert PDFs to images and vice versa with the help of Cloudinary. There are additional formats we can convert between with additional options. Feel free to review the resources to read on what's possible.