Skip to content

Instantly share code, notes, and snippets.

@iamnasirudeen
Created May 14, 2020 19:27
Show Gist options
  • Save iamnasirudeen/235fa8bd3068b382947ada024d6123d6 to your computer and use it in GitHub Desktop.
Save iamnasirudeen/235fa8bd3068b382947ada024d6123d6 to your computer and use it in GitHub Desktop.
import AWS from "aws-sdk";
import { v2 as cloudinary } from "cloudinary";
import crypto from "crypto";
import fs from "fs";
import Jimp from "jimp";
import path from "path";
import gifsicle from "gifsicle";
import { execFile } from "child_process";
import sizeOf from "image-size";
import Settings from "../models/settings";
/**
* Upload file to either Local or Online Storage.
*
* @param {Object} options ***Options** can either be amazons3, cloudinary or local, options will
* also contain a **config** object (this takes in the API and secret keys of each service).
*
* @param {Object} file ***File:** The file to be uploaded. You can pass a file by using any
* of your favorite file passing module e.g
* * Multer,
* * Formidable.
*
* Just parse in req.file as file
* e.g: **new Upload(options, req.file, image_width, quality)**
* @param {Number} image_width This is the width of the new image.
* @param {Number} quality This is the quality of the new image.
* @param {Boolean} delete_file Will determine if file should be deleted yet from the temp folder.
* @param {Number} image_height A custom image height.
*/
class Upload {
constructor(options, file, image_width, quality, delete_file, image_height) {
this.options = options;
this.file = file;
this.image_width = image_width;
this.image_height =
image_height == undefined
? Jimp.AUTO
: sizeOf(this.file.path).height < image_height
? Jimp.AUTO
: image_height;
this.quality = quality;
this.delete_file = delete_file;
}
/**
* check if file is an svg.
*/
is_svg() {
return this.file.mimetype.indexOf("svg") > -1;
}
/**
* Handler to upload and save files to local storage
*/
toLocalStorage() {
return new Promise(async (resolve, reject) => {
try {
const name = Date.now() + path.extname(this.file.originalname);
const dest = path.join(__dirname, "..", "public", "media", name);
// if file type is not an image, use this
if (this.file.mimetype.indexOf("image") < 0) {
const data = fs.readFileSync(this.file.path);
fs.writeFileSync(dest, data);
if (this.delete_file) fs.unlinkSync(this.file.path);
resolve(name);
} else {
// check if file is a type of gif
if (this.file.mimetype.indexOf("gif") > -1) {
execFile(
gifsicle,
[
"--resize-fit-width",
this.image_width,
"-o",
dest,
this.file.path
],
err => {
if (err) reject(err);
resolve(name);
}
);
}
// check if file is svg, just upload it, no processing for SVG for now
if (this.is_svg()) {
const data = fs.readFileSync(this.file.path);
fs.writeFileSync(dest, data);
if (this.delete_file) fs.unlinkSync(this.file.path);
resolve(name);
}
// check if file type is not gif
if (this.file.mimetype.indexOf("gif") < 0) {
// Resize image before writing to path
let image = await Jimp.read(this.file.path);
image.resize(this.image_width, this.image_height);
image.quality(this.quality);
await image.writeAsync(dest);
if (this.delete_file) fs.unlinkSync(this.file.path);
resolve(name);
}
}
} catch (error) {
reject(error);
}
});
}
/**
*
* @param {String} path The path to the file to be uploaded.
*/
__aws(path) {
let s3 = new AWS.S3({
accessKeyId: this.options.config.amazons3.accessKeyId,
secretAccessKey: this.options.config.amazons3.secretAccessKey,
bucket: this.options.config.amazons3.bucket
});
let stream = fs.createReadStream(path);
let params = {
Bucket: this.options.config.amazons3.bucket,
Key: Date.now() + "-" + this.file.originalname,
Body: stream,
ContentType: this.file.mimetype,
ACL: "public-read",
processData: false
};
let that = this;
return new Promise((resolve, reject) => {
s3.upload(params, function(err, data) {
if (that.file.mimetype.indexOf("image") > -1) {
if (that.delete_file) {
fs.unlinkSync(that.file.path);
fs.unlinkSync(path);
}
} else {
if (that.delete_file) fs.unlinkSync(path);
}
if (err) reject(err);
else resolve(data.Location);
});
});
}
/**
* Handler to upload to AWS S3
*/
async toS3() {
if (this.file.mimetype.indexOf("image") < 0)
return await this.__aws(this.file.path);
if (this.is_svg()) return await this.__aws(this.file.path);
if (this.file.mimetype.indexOf("image") > -1) {
if (this.file.mimetype.indexOf("gif") < 0) {
// resize and optimize image
let random =
this.file.path + " " + crypto.randomBytes(4).toString("hex");
let image = await Jimp.read(this.file.path);
image.resize(this.image_width, this.image_height);
image.quality(this.quality);
await image.writeAsync(random);
// upload to cloudinary
return await this.__aws(random);
}
if (this.file.mimetype.indexOf("gif") > -1) {
return new Promise((resolve, reject) => {
const new_path = this.file.path + "-" + "optimized-image";
execFile(
gifsicle,
[
"--resize-fit-width",
this.image_width,
"-o",
new_path,
this.file.path
],
async err => {
if (err) reject(err);
return resolve(this.__aws(new_path));
}
);
});
}
}
}
/**
*
* @param {String} path The path to the file to be uploaded.
*/
__cloud(path) {
return new Promise((resolve, reject) => {
cloudinary.uploader.upload_large(
path,
{
resource_type: "raw",
public_id: Date.now() + "-" + this.file.originalname
},
(err, result) => {
if (err) reject(err);
if (this.file.mimetype.indexOf("image") > -1) {
if (this.delete_file) {
fs.unlinkSync(this.file.path);
fs.unlinkSync(path);
}
} else {
if (this.delete_file) fs.unlinkSync(path);
}
resolve(result.secure_url);
}
);
});
}
/**
* Handler to upload files to cloudinary
*/
async toCloudinary() {
cloudinary.config({
cloud_name: this.options.config.cloudinary.cloud_name,
api_key: this.options.config.cloudinary.api_key,
api_secret: this.options.config.cloudinary.api_secret
});
if (this.file.mimetype.indexOf("image") < 0)
return this.__cloud(this.file.path);
if (this.is_svg()) return this.__cloud(this.file.path);
if (this.file.mimetype.indexOf("image") > -1) {
let random = this.file.path + " " + crypto.randomBytes(4).toString("hex");
if (this.file.mimetype.indexOf("gif") < 0) {
// resize and optimize image
let image = await Jimp.read(this.file.path);
image.resize(this.image_width, this.image_height);
image.quality(this.quality);
await image.writeAsync(random);
// upload to cloudinary
return this.__cloud(random);
}
if (this.file.mimetype.indexOf("gif") > -1) {
return new Promise((resolve, reject) => {
const new_path = this.file.path + "-" + "optimized-image";
execFile(
gifsicle,
[
"--resize-fit-width",
this.image_width,
"-o",
new_path,
this.file.path
],
async err => {
if (err) reject(err);
return resolve(this.__cloud(new_path));
}
);
});
}
}
}
}
/**
* This handles the file upload service config by checking the database for their Secret Keys
* and checking which service to upload a file to.
*
* @param {Object} file The file to be uploaded.
* @param {Number} image_width The width of the new image.
* @param {Number} quality The quality of the new image.
* @param {Boolean} delete_file Will determine if file should be deleted yet from the temp folder.
* @param {Number} image_height A custom image height.
*/
async function _Upload(file, image_width, quality, delete_file, image_height) {
const set = await Settings.findOne();
const { accessKeyId, secretAccessKey, bucket } = set.media.config.amazons3;
const { cloud_name, api_key, api_secret } = set.media.config.cloudinary;
const options = {
config: {
amazons3: {
accessKeyId,
secretAccessKey,
bucket
},
cloudinary: {
cloud_name,
api_key,
api_secret
}
}
};
if (set.media.provider == "local")
return new Upload(
options,
file,
image_width,
quality,
delete_file,
image_height
).toLocalStorage();
if (set.media.provider == "amazons3")
return new Upload(
options,
file,
image_width,
quality,
delete_file,
image_height
).toS3();
if (set.media.provider == "cloudinary")
return new Upload(
options,
file,
image_width,
quality,
delete_file,
image_height
).toCloudinary();
}
export default _Upload;
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment