Days
Hours
Minutes
Seconds
x

Froala Editor v4.1.4 is Here LEARN MORE

Skip to content

Node.JS

Image Upload

The following sections describe how to handle image uploads on your server using Node.JS as a server-side language. For information on the upload workflow refer to the image upload documentation.

Dependencies

The node.JS image upload example requires the following dependencies:

Make sure that you run npm install, or if you are using bower run bower install

Frontend

Setting up the index page.

  1. On the head section include the Editor style.

  2. <!DOCTYPE html>
      <html>
      <head>
      <meta charset="utf-8">
      
      <link href="https://cdn.jsdelivr.net/npm/froala-editor@latest/css/froala_editor.pkgd.min.css" rel="stylesheet" type="text/css" />
      </head>
  3. On the body section include the Editor JS files and define the area for the editor.

  4.   <body>  
      <script type="text/javascript" src="https://cdn.jsdelivr.net/npm/froala-editor@latest/js/froala_editor.pkgd.min.js"></script>
      
      <div class="sample">
      <h2>image upload example.</h2>
      <form>
      <textarea id="edit" name="content"></textarea>
      </form>
      </div>
      
  5. Initialize the editor and set the image upload URL

  6.   <script>
      new FroalaEditor('#edit', {
      // Set the image upload URL.
      imageUploadURL: '/UploadFiles',
      
      imageUploadParams: {
      id: 'my_editor'
      }
      })
      </script>
      </body>
      </html>

The full code should look like this:

<!DOCTYPE html>
  <html>
  <head>
  <meta charset="utf-8">
  <link href="https://cdn.jsdelivr.net/npm/froala-editor@latest/css/froala_editor.pkgd.min.css" rel="stylesheet" type="text/css" />
  </head>
  
  <body>
  <script type="text/javascript" src="https://cdn.jsdelivr.net/npm/froala-editor@latest/js/froala_editor.pkgd.min.js"></script>
  
  <div class="sample">
  <h2>image upload example.</h2>
  <form>
  <textarea id="edit" name="content"></textarea>
  </form>
  </div>
  
  
  <script>
  new FroalaEditor('#edit', {
  
  imageUploadURL: '/UploadFiles',
  
  imageUploadParams: {
  id: 'my_editor'
  }
  })
  </script>
  </body>
  </html>

The full code should look like this:

<!DOCTYPE html>
    <html>
    <head>
    <meta charset="utf-8">
    <link href="https://cdn.jsdelivr.net/npm/froala-editor@latest/css/froala_editor.pkgd.min.css" rel="stylesheet" type="text/css" />
    </head>
    
    <body>
    <script type="text/javascript" src="https://cdn.jsdelivr.net/npm/froala-editor@latest/js/froala_editor.pkgd.min.js"></script>
    
    <div class="sample">
    <h2>Image upload example.</h2>
    <form>
    <textarea id="edit" name="content"></textarea>
    </form>
    </div>
    
    
    <script>
    new FroalaEditor('#edit', {
    
    imageUploadURL: '/UploadFiles',
    
    fileUploadParams: {
    id: 'my_editor'
    }
    })
    </script>
    </body>
    </html>

Backend

server.js handles the server part for Node.JS

server.js handles both image and file upload

// Include required libs
  var express = require("express");
  var app = express();
  var bodyParser = require("body-parser")
  var path = require("path");
  var fs = require("fs");
  var upload_file = require("./file_upload.js");
  var upload_image = require("./image_upload.js");
  
  app.use(express.static(__dirname + "/"));
  app.use(bodyParser.urlencoded({ extended: false }));
  
  app.get("/", function(req, res) {
  res.sendFile(__dirname + "/index.html");
  });
  
  // File POST handler.
  app.post("/file_upload", function (req, res) {
  upload_file(req, function(err, data) {
  
  if (err) {
  return res.status(404).end(JSON.stringify(err));
  }
  
  res.send(data);
  });
  });
  
  // Image POST handler.
  app.post("/image_upload", function (req, res) {
  upload_image(req, function(err, data) {
  
  if (err) {
  return res.status(404).end(JSON.stringify(err));
  }
  
  res.send(data);
  });
  });
  
  // Create folder for uploading files.
  var filesDir = path.join(path.dirname(require.main.filename), "uploads");
  
  if (!fs.existsSync(filesDir)){
  fs.mkdirSync(filesDir);
  }
  
  // Init server.
  app.listen(3000, function () {
  console.log("Example app listening on port 3000!");
  });

image_upload.js handles the upload part. It has basic image format validations that can be easily extended.

The uploads directory must be set to a valid location before making uploads. The path can be any folder that is accessible and writable.

If the uploaded image passes the validation step, the server responds with a JSON object containing a link to the uploaded file.

E.g.: {"link":"http://server_address/uploads/name_of_file"}

var Busboy = require("busboy");
  var path = require("path");
  var fs = require("fs");
  var sha1 = require("sha1");
  
  // Gets a filename extension.
  function getExtension(filename) {
  return filename.split(".").pop();
  }
  
  // Test if a image is valid based on its extension and mime type.
  function isImageValid(filename, mimetype) {
  var allowedExts = ["gif", "jpeg", "jpg", "png", "svg", "blob"];
  var allowedMimeTypes = ["image/gif", "image/jpeg", "image/pjpeg", "image/x-png", "image/png", "image/svg+xml"];
  
  // Get image extension.
  var extension = getExtension(filename);
  
  return allowedExts.indexOf(extension.toLowerCase()) != -1  &&
  allowedMimeTypes.indexOf(mimetype) != -1;
  }
  
  function upload (req, callback) {
  // The route on which the image is saved.
  var fileRoute = "/uploads/";
  
  // Server side file path on which the image is saved.
  var saveToPath = null;
  
  // Flag to tell if a stream had an error.
  var hadStreamError = null;
  
  // Used for sending response.
  var link = null;
  
  // Stream error handler.
  function handleStreamError(error) {
  // Do not enter twice in here.
  if (hadStreamError) {
  return;
  }
  
  hadStreamError = error;
  
  // Cleanup: delete the saved path.
  if (saveToPath) {
  return fs.unlink(saveToPath, function (err) {
  return callback(error);
  });
  }
  
  return callback(error);
  }
  
  // Instantiate Busboy.
  try {
  var busboy = new Busboy({ headers: req.headers });
  } catch(e) {
  return callback(e);
  }
  
  // Handle file arrival.
  busboy.on("file", function(fieldname, file, filename, encoding, mimetype) {
  // Check fieldname:
  if ("file" != fieldname) {
  // Stop receiving from this stream.
  file.resume();
  return callback("Fieldname is not correct. It must be "file".");
  }
  
  // Generate link.
  var randomName = sha1(new Date().getTime()) + "." + getExtension(filename);
  link = fileRoute + randomName;
  
  // Generate path where the file will be saved.
  var appDir = path.dirname(require.main.filename);
  saveToPath = path.join(appDir, link);
  
  // Pipe reader stream (file from client) into writer stream (file from disk).
  file.on("error", handleStreamError);
  
  // Create stream writer to save to file to disk.
  var diskWriterStream = fs.createWriteStream(saveToPath);
  diskWriterStream.on("error", handleStreamError);
  
  // Validate image after it is successfully saved to disk.
  diskWriterStream.on("finish", function() {
  // Check if image is valid
  var status = isImageValid(saveToPath, mimetype);
  
  if (!status) {
  return handleStreamError("File does not meet the validation.");
  }
  
  return callback(null, {link: link});
  });
  
  // Save image to disk.
  file.pipe(diskWriterStream);
  });
  
  // Handle file upload termination.
  busboy.on("error", handleStreamError);
  req.on("error", handleStreamError);
  
  // Pipe reader stream into writer stream.
  return req.pipe(busboy);
  }
  
  module.exports = upload;
  

Do you think we can improve this article? Let us know.

[class^="wpforms-"]
[class^="wpforms-"]
[bws_google_captcha]
<div class="gglcptch gglcptch_v2"><div id="gglcptch_recaptcha_280868826" class="gglcptch_recaptcha"></div> <noscript> <div style="width: 302px;"> <div style="width: 302px; height: 422px; position: relative;"> <div style="width: 302px; height: 422px; position: absolute;"> <iframe src="https://www.google.com/recaptcha/api/fallback?k=6Ld6lNoUAAAAAM626LfCOrnkBFJtYZAKESFCjgv_" frameborder="0" scrolling="no" style="width: 302px; height:422px; border-style: none;"></iframe> </div> </div> <div style="border-style: none; bottom: 12px; left: 25px; margin: 0px; padding: 0px; right: 25px; background: #f9f9f9; border: 1px solid #c1c1c1; border-radius: 3px; height: 60px; width: 300px;"> <textarea id="g-recaptcha-response" name="g-recaptcha-response" class="g-recaptcha-response" style="width: 250px !important; height: 40px !important; border: 1px solid #c1c1c1 !important; margin: 10px 25px !important; padding: 0px !important; resize: none !important;"></textarea> </div> </div> </noscript></div>
[class^="wpforms-"]
[class^="wpforms-"]
[bws_google_captcha]
<div class="gglcptch gglcptch_v2"><div id="gglcptch_recaptcha_851736084" class="gglcptch_recaptcha"></div> <noscript> <div style="width: 302px;"> <div style="width: 302px; height: 422px; position: relative;"> <div style="width: 302px; height: 422px; position: absolute;"> <iframe src="https://www.google.com/recaptcha/api/fallback?k=6Ld6lNoUAAAAAM626LfCOrnkBFJtYZAKESFCjgv_" frameborder="0" scrolling="no" style="width: 302px; height:422px; border-style: none;"></iframe> </div> </div> <div style="border-style: none; bottom: 12px; left: 25px; margin: 0px; padding: 0px; right: 25px; background: #f9f9f9; border: 1px solid #c1c1c1; border-radius: 3px; height: 60px; width: 300px;"> <textarea id="g-recaptcha-response" name="g-recaptcha-response" class="g-recaptcha-response" style="width: 250px !important; height: 40px !important; border: 1px solid #c1c1c1 !important; margin: 10px 25px !important; padding: 0px !important; resize: none !important;"></textarea> </div> </div> </noscript></div>
[class^="wpforms-"]
[class^="wpforms-"]
[bws_google_captcha]
<div class="gglcptch gglcptch_v2"><div id="gglcptch_recaptcha_118016086" class="gglcptch_recaptcha"></div> <noscript> <div style="width: 302px;"> <div style="width: 302px; height: 422px; position: relative;"> <div style="width: 302px; height: 422px; position: absolute;"> <iframe src="https://www.google.com/recaptcha/api/fallback?k=6Ld6lNoUAAAAAM626LfCOrnkBFJtYZAKESFCjgv_" frameborder="0" scrolling="no" style="width: 302px; height:422px; border-style: none;"></iframe> </div> </div> <div style="border-style: none; bottom: 12px; left: 25px; margin: 0px; padding: 0px; right: 25px; background: #f9f9f9; border: 1px solid #c1c1c1; border-radius: 3px; height: 60px; width: 300px;"> <textarea id="g-recaptcha-response" name="g-recaptcha-response" class="g-recaptcha-response" style="width: 250px !important; height: 40px !important; border: 1px solid #c1c1c1 !important; margin: 10px 25px !important; padding: 0px !important; resize: none !important;"></textarea> </div> </div> </noscript></div>