In Part 1, I set up the mean.io app and tweaked a few things. Now, we can dive into some coding hopefully.
The imageProcessor package
The idea is to create a little image processing app that takes a batch of files in the front end and then processes them (using ImageMagick) on the back-end to resize them, or whatever.
The main thrust here is to
- Figure out how to use Mean.io
- Create a package in mean.io
- Use kue to do the job processing (using redis for job queuing)
- Use MongoDB to handle the large data handling
Create the package
Mean.io makes this pretty simple
mean package imageProcessor
Voila! A bunch of files are already set up for you in packages/custom/imageProcessor
Defining the API
URL | Method | Description |
---|---|---|
/jobs | GET | List all jobs assigned to logged in user, along with their status, and if they’re complete, with a link to be able to download the results |
/jobs/new | POST | Submit a new job with attached data |
/job/:id | GET DELETE | Get a detailed summary & status of indicated job; delete job and data |
/job/:id/results | GET | Retrieve the results for job :id |
/job/:id/cancel | DELETE | Cancel a (presumably) running job. Has no effect otherwise |
Database design
Based on the above API, I need to define schemas for the job queue and imageProcessor Models.
The Redis job queue will carry only the following information:
Field | Type | Description |
---|---|---|
user | user name | |
timeJobStarted | timeStamp | |
jobComplete | boolean | True if job is complete and results are ready |
jobFailed | int | The number of times the job has failed |
data | object | if no large data is required, additional data can be stored here |
mongoId | string | Link to the MongoDoc for extra infos |
The JobId is a timestamp String followed by a random 8 digit code.
The job type will always be ‘imageprocessor’ for this demo.
Each job type will have its own Schema associated with it. For this demo, the MongoDB entries for the ImageProcessor job type will have the following schema:
Field | Type | Description |
---|---|---|
_id | Autogenerated | |
user | The user | |
command | string | the type of imageMagick command (resize, etc) |
command_parms | string | the parameters to pass to imageMagick |
image | blob | the actual image data |
results | blob | the results of the command |
This is implemented as a Mongoose schema in the models
folder of the custom package.
I also added some validations on the parameter value that depends on the actual command issued. This requires us to add a hook after basic validation (so that the values are available) but before the document is saved.
ImageProcessorSchema.pre('save', function(next, doc) {
var err = null;
var command = doc.command;
var parms = doc.command_parms;
switch (command) {
case 'resize':
//Param must be of form nnnxnnn
err = /^\d+x\d+$/.test(parms)? null : new Error("Invalid image size specifications: " + parms);
break;
case 'convert':
err = /^(png|jpg|gif)$/.test("png")? null : new Error("Invalid image format specification: " + parms);
break;
default:
err = new Error("Invalid command: " + command);
}
next(err);
});
Note the pretty neat error handling pattern. Whether or not an error condition exists, you call next
with err
(it’s
null, or contains an Error object) and if there is an error, it will get handled in an appropriate place.