Securely upload and view image in Node JS and Mongo DB

Learn how to securely upload and view image in Node JS and Mongo DB. Most of the time when we create a website where we allow users to upload images, we face a problem to secure them from unauthorized access. For example, you are creating a website where you are allowing users to upload pictures and share them with only selected people. Now you do not want any other person to view that picture.

This can be done easily with Node JS. First, we are going to create an empty project. So run the following commands in your terminal.

> npm init
> npm install express express-formidable ejs fs mongodb
> npm install -g nodemon
> nodemon server.js

Upload image

After that, create a file named server.js and write the following code in it.

// server.js

const express = require("express")
const app = express()
app.set("view engine", "ejs")

const port = process.env.PORT || 3000
app.listen(port, function () {
    console.log("Server started.")

    app.get("/", function (request, result) {
      result.render("index")
    })
})

Now, we will create a folder named views and inside this folder, create a file named index.ejs. Following will be the content of this file.

<!-- views/index.ejs -->

<form method="POST" action="/upload" enctype="multipart/form-data">
  <input type="file" name="file" />
  <input type="submit" value="Upload" />
</form>

This will create a form where user can select image. You can access this from the URL:

http://localhost:3000

Now in our server.js file, we will include the file system module, express formidable module and connect Mongo DB.

// server.js

const fs = require("fs")
const mongodb = require("mongodb")
const ObjectId = mongodb.ObjectId
const mongoClient = new mongodb.MongoClient("mongodb://localhost:27017")

const expressFormidable = require("express-formidable")
app.use(expressFormidable())

// Connect the client to the server (optional starting in v4.7)
await mongoClient.connect()

// Establish and verify connection
const db = await mongoClient.db("upload_view")
db.command({ ping: 1 })
console.log("Database connected")

After that, we will create a route that will handle this request.

// server.js

app.post("/upload", async function (request, result) {
  const file = request.files.file

  const fileData = await fs.readFileSync(file.path)
  if (!fileData) {
    console.error(fileData)
    return
  }

  const filePath = "uploads/" + (new Date().getTime()) + "-" + file.name
  fs.writeFileSync(filePath, fileData)

  await db.collection("files").insertOne({
    path: filePath,
    name: file.name,
    size: file.size
  })

  result.send("File has been uploaded.")
})

This will upload the file in your uploads folder. If you do not have that folder, you need to create it. This will also save the uploaded file data in Mongo DB.

You will not be able to view the images directly from the URL in the browser. So we need to create an API allowing users to view the image.

View image

First, we will fetch all the images from Mongo DB. So change your main route to the following:

// server.js

app.get("/", async function (request, result) {
  const files = await db.collection("files").find({}).toArray()
  result.render("index", {
    files: files
  })
})

Now, in your views/index.ejs, we will loop through all images and display their name and a link to view.

<!-- views/index.ejs -->

<% files.forEach(function (file) { %>
  <p>
    <a href="image/<%= file._id %>">
      <%= file.name %>
    </a>

    <img src="image/<%= file._id %>" style="width: 300px;" />
  </p>
<% }) %>

Finally, we need to create an API that will return the content of image. This will allow you to view the image on user side.

// server.js

app.get("/image/:_id", async function (request, result) {
  const _id = request.params._id
  const file = await db.collection("files").findOne({
    _id: new ObjectId(_id)
  })

  if (file == null) {
    result.json({
      status: "error",
      message: "File not found."
    })
    return
  }

  const fileData = await fs.readFileSync(file.path)
  if (!fileData) {
    console.error(fileData)
    return
  }
  result.writeHead(200, {
    "Content-Type": "image/png",
    "Content-Length": fileData.length
  })
  result.end(fileData)
})

Now you will be able to view the image as well.

Watermark image after upload – PHP, No Library

In this tutorial, we will teach you how you can put a watermark on an image uploaded by a user. You might have seen websites that allow you to upload images, but when you try to download them, they put their watermark on your image and ask you to pay to remove it. So let’s learn how they do this.

First I am going to create a form where we will show the input type file to select the image. Its method will be POST and the action will be “index.php”. The encoding (enctype) type must be multipart/form-data. This is necessary for uploading files. Then we are going to show a label that says “select file” and an input type file. The name attribute on the input type file will be used on the PHP side. It will also have an attribute accept=”image/*”, which means this input field will accept only image type, for example, PNG or JPEG, etc, but no PDF or DOCX type of files will be selected in this field. Finally, the form will have a submit button. Submit button will have a name attribute so we can know when this form is submitted.

Select a file

<form method="POST" action="index.php" enctype="multipart/form-data">

	<p>
		<label>Select file</label>
		<input type="file" name="image" accept="image/*" />
	</p>

	<input type="submit" name="submit" value="Upload" />
</form>

If you run the code now, you will see a form with an input type file and a submit button. Now we need to write the PHP code to handle this form.

Apply the watermark

First, we will check if the form is submitted.

<?php

if (isset($_POST["submit"]))

Then we will save the user uploaded image in a file named “image.png” because, in order to put a watermark on it, the file must be stored in our server.

move_uploaded_file($_FILES["image"]["tmp_name"], "image.png");

Then we need to get this image as an object. So call imagecreatefromjpeg function if your image was a JPEG image, imagecreatefrompng if it is a PNG. Parameter will be the path of saved image file.

$image_obj = imagecreatefromjpeg("image.png");

Similarly, we will create an object of our watermark image, which is a PNG image.

$watermark = imagecreatefrompng("watermark.png");

Then we will define the margin from the right and from the bottom, since we do not want the watermark to stick to the corners, we want a little margin.

$margin_right = 10;
$margin_bottom = 10;

Then we are going to get the watermark image width and height in a separate variable.

$watermark_width = imagesx($watermark);
$watermark_height = imagesy($watermark);

Now comes the tricky part. We need to tell the X and Y coordinates of original image where we will place the watermark. The X position from left will be width of uploaded image, minus width of watermark image, minus margin from right.

$destination_x = imagesx($image_obj) - $watermark_width - $margin_right;

Similarly, the Y position from top will be height of uploaded image, minus height of watermark image, minus margin from bottom.

$destination_y = imagesy($image_obj) - $watermark_height - $margin_bottom;

Now we need to copy the watermark image in user uploaded image. The syntax of this function is:

// imagecopy(dst_im, src_im, dst_x, dst_y, src_x, src_y, src_w, src_h)

Our destination image is the image uploaded by user. Source image is the watermark image. Destination x and y are just we calculated, I will explain this as well. And source x and y will be zero, because we do not want any margin in our watermark image. And finally the width and height of our source image which is watermark image.

imagecopy($image_obj, $watermark, $destination_x, $destination_y, 0, 0, $watermark_width, $watermark_height);

Now let me explain the calculation we did above.

Explaining X, Y co-ordinates

width of user image = 2560
width of watermark image = 640
margin from right = 10

2560 - 640 - 10 = 1910 = x

height of user image = 1600
height of watermark image = 345
margin from bottom = 10

1600 - 345 - 10 = 1245 = y

First is destination_x variable. The width of image object is 2560px, minus the width of watermark is 640px, minus the margin right which is 10. That equals 1910, so our watermark will be placed at 1910 on X axis from left. Now come to destination_y variable, height of user uploaded image is 1600px, it will be different for each image because user can upload image of any resolution. I am writing these values as sample. So the 1600px is the height of user uploaded image. Minus the height of watermark, which is 345px. Minus margin from bottom that is 10. That equals 1245. So our watermark image will be placed at 1245 on Y axis from top.

1910 + 640 = 2550
1245 + 345 = 1590

Now when the watermark is placed at 1910 from left and it has a width of 640px, then that equals 2550 which is 10px less than the width of user image, which is 2560px. So there is a difference of 10px, which means there will be a margin of 10px from right. Similarly, when the watermark is placed at 1245 from the top and it has a height of 345px, then that equals 1590 which is again 10px less than the height of the user image, which is 1600px. So it will have a margin of 10px from the bottom.

Generating watermarked image

Now the watermark image object has been copied to the user uploaded image object using imagecopy function above. Now we need to generate that image.

imagepng($image_obj, "output.png");

The first parameter will be an image object, which is a user object along with a watermark object because the watermark image has been copied to it. And second, will be the path of the output file.

Finally, we are going to destroy the image objects we just created for user-uploaded images and watermark image to free up the memory.

imagedestroy($image_obj);
imagedestroy($watermark);

Also, we saved the user image using the move uploaded file function because we need the image to be stored before putting a watermark on it. Now the watermark is placed, so we have no need for the original user-selected picture, so we are going to delete it using the unlink function.

unlink("image.png");

If you run the code now, select an image, and upload it, you will see that the original image was saved as image.png and the output.png is being rendered for a few seconds. When the output.png is completely rendered then the original image.png was automatically deleted because we have used the unlink function.

Complete code

<?php

if (isset($_POST["submit"]))
{
	move_uploaded_file($_FILES["image"]["tmp_name"], "image.png");
	$image_obj = imagecreatefromjpeg("image.png");

	$watermark = imagecreatefrompng("watermark.png");

	$margin_right = 10;
	$margin_bottom = 10;

	$watermark_width = imagesx($watermark);
	$watermark_height = imagesy($watermark);

	$destination_x = imagesx($image_obj) - $watermark_width - $margin_right;
	$destination_y = imagesy($image_obj) - $watermark_height - $margin_bottom;

	// imagecopy(dst_im, src_im, dst_x, dst_y, src_x, src_y, src_w, src_h)
	imagecopy($image_obj, $watermark, $destination_x, $destination_y, 0, 0, $watermark_width, $watermark_height);

	// 2560 - 640 - 10 = 1910
	// 1600 - 345 - 10 = 1245

	// x = 1910 + 640 = 2550
	// y = 1245 + 345 = 1590

	imagepng($image_obj, "output.png");

	imagedestroy($image_obj);
	imagedestroy($watermark);

	unlink("image.png");
}

?>

The final image will be of the same quality as of original image. If you face any problems in following this, kindly mention them in the comments section below.

How you can also resize the image without stretching in PHP by following this tutorial.

[wpdm_package id=’1262′]