Created
May 14, 2020 19:27
-
-
Save iamnasirudeen/235fa8bd3068b382947ada024d6123d6 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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