Skip to content

06. Uploading to Cloudinary

Nick Doiron edited this page Dec 6, 2017 · 2 revisions

There are cloud file systems, CDNs, etc where you can store files. There are also services which use these same services, but have an easier interface for coders. In this case, I've decided to use Cloudinary to make multiple sizes and shapes of photos available.

Install

Set up a Cloudinary account, then install their Node module and a file POST helper, busboy:

npm install cloudinary async-busboy --save

Cloudinary comes with some different environment variables, set automatically on Heroku.

Code Config

In a new uploads.js file, import the modules

// uploads.js
const cloudinary = require('cloudinary');
const asyncBusboy = require('async-busboy')

...
if (!process.env.CLOUDINARY_URL) {
  // CLOUDINARY_URL is the method used automatically by Heroku
  // if you're elsewhere, have these variables:
  cloudinary.config({
    cloud_name: process.env.CLOUD_NAME,
    api_key: process.env.CLOUDINARY_API_KEY,
    api_secret: process.env.CLOUDINARY_API_SECRET
  });
}

Uploading an image

Client

// include enctype="multipart/form-data" whenever you are uploading files
// also include the CSRF variable directly in the action URL - it helps!
form(action="/upload?_csrf=" + csrfToken, method="POST", enctype="multipart/form-data")
  input(type="file", name="upload")
  input(type="submit", value="Upload")

Database

You should create an Image model to record any uploaded images and connect them to usernames

const mongoose = require('mongoose');

var imageSchema = mongoose.Schema({
  user_id: String,
  src: String
});

module.exports = mongoose.model('Image', imageSchema);

Server

Like you did for logins, pass the app and router to uploads.js

// app.js
const setupUploads = require('./uploads.js');
setupUploads(app, router);

Back in uploads.js:

module.exports = function (app, router) {
  router.post('/upload', async function (ctx) {
    var requser = ctx.req.user;
    if (!requser) {
      // not logged in
      return ctx.redirect('/login');
    }

    // use new syntax to receive multiple results from asyncBusboy
    var {files, fields} = await asyncBusboy(ctx.req);

    // only single file upload for now
    if (files.length !== 1) {
      return ctx.redirect('/profile');
    }
    var file = files[0];

    // send to cloudinary, and wait for upload to finish before going back to profile
    await cloudinary.uploader.upload(file.path, async function(result) {
      // create record in MongoDB
      var i = new Image({
        user_id: requser.name,
        src: result.public_id
      });
      await i.save();
    }, { public_id: Math.random() + "_" + (new Date() * 1) } );

    // see the new profile
    ctx.redirect('/profile');
  });
}

Clone this wiki locally