158 lines
4.9 KiB
JavaScript
158 lines
4.9 KiB
JavaScript
const md5 = require('md5');
|
|
const fs = require('fs-extra');
|
|
const sharp = require('sharp');
|
|
|
|
module.exports = Self => {
|
|
Self.remoteMethod('upload', {
|
|
description: 'Uploads an image',
|
|
accessType: 'WRITE',
|
|
accepts: [
|
|
{
|
|
arg: 'ctx',
|
|
type: 'Object',
|
|
http: {source: 'context'}
|
|
}
|
|
],
|
|
returns: {
|
|
type: Self.modelName,
|
|
description: 'The resulting file instance',
|
|
root: true,
|
|
},
|
|
http: {
|
|
path: `/upload`,
|
|
verb: 'POST'
|
|
}
|
|
});
|
|
|
|
Self.upload = async ctx => {
|
|
let app = Self.app;
|
|
let $ = app.models;
|
|
let storageConnector = app.dataSources.storage.connector;
|
|
|
|
let tx = await Self.beginTransaction({});
|
|
let myOptions = {transaction: tx};
|
|
|
|
async function getContainer(name) {
|
|
let container;
|
|
try {
|
|
container = await $.Container.getContainer(name);
|
|
} catch (err) {
|
|
if (err.code === 'ENOENT') {
|
|
container = await $.Container.createContainer({
|
|
name: name
|
|
});
|
|
} else throw err;
|
|
}
|
|
|
|
return container;
|
|
}
|
|
|
|
try {
|
|
// Upload file to temporary path
|
|
|
|
let tempContainer = await getContainer('temp');
|
|
let uploaded = await $.Container.upload(tempContainer.name, ctx.req, ctx.result, {});
|
|
let files = Object.values(uploaded.files).map(file => file[0]);
|
|
let args = {};
|
|
|
|
for (let key in uploaded.fields)
|
|
args[key] = uploaded.fields[key][0];
|
|
|
|
if (!/^[a-z0-9_]+$/.test(args.name))
|
|
throw new Error('Bad file name');
|
|
|
|
let collection = await $.ImageCollection.findOne({
|
|
fields: [
|
|
'id',
|
|
'name',
|
|
'maxWidth',
|
|
'maxHeight',
|
|
'model',
|
|
'property'
|
|
],
|
|
where: {name: args.collectionFk},
|
|
include: {
|
|
relation: 'sizes',
|
|
scope: {
|
|
fields: ['width', 'height', 'crop']
|
|
}
|
|
}
|
|
});
|
|
|
|
let md5Hash = md5(args.name).substring(0, 4);
|
|
let md5Path = md5Hash.match(/(..?)/g).join('/');
|
|
let rootPath = storageConnector.client.root;
|
|
|
|
let file = files[0];
|
|
let data = {
|
|
name: args.name,
|
|
collectionFk: args.collectionFk
|
|
};
|
|
let newImage = await Self.upsertWithWhere(data, {
|
|
name: args.name,
|
|
collectionFk: args.collectionFk,
|
|
updated: (new Date).getTime()
|
|
}, myOptions);
|
|
|
|
// Resizes and saves the image
|
|
|
|
let extension = file.name.split('.').pop().toLowerCase();
|
|
let srcPath = `${rootPath}/${tempContainer.name}/${file.name}`;
|
|
let collectionDir = `${rootPath}/${args.collectionFk}`;
|
|
let dstDir = `${collectionDir}/${md5Path}/${args.name}`;
|
|
let dstFile = `full.${extension}`;
|
|
|
|
let resizeOpts = {
|
|
withoutEnlargement: true,
|
|
fit: 'inside'
|
|
};
|
|
|
|
await fs.mkdir(dstDir, {recursive: true});
|
|
await sharp(srcPath)
|
|
.resize(collection.maxWidth, collection.maxHeight, resizeOpts)
|
|
.toFile(`${dstDir}/${dstFile}`);
|
|
|
|
let sizes = collection.sizes();
|
|
for (let size of sizes) {
|
|
let dstFile = `${size.width}x${size.height}.${extension}`;
|
|
let resizeOpts = {
|
|
withoutEnlargement: true,
|
|
fit: size.crop ? 'cover' : 'inside'
|
|
};
|
|
|
|
await sharp(srcPath)
|
|
.resize(size.width, size.height, resizeOpts)
|
|
.toFile(`${dstDir}/${dstFile}`);
|
|
}
|
|
|
|
// Updates items with matching id, when option is checked
|
|
|
|
if (args.updateMatching === 'true') {
|
|
if (!collection.model || !collection.property)
|
|
throw new Error('Matching model settings not defined');
|
|
|
|
let model = app.models[collection.model];
|
|
|
|
if (!model)
|
|
throw new Error('Matching model not found');
|
|
|
|
let item = await model.findById(args.name, null, myOptions);
|
|
|
|
if (item)
|
|
await item.updateAttribute(
|
|
collection.property,
|
|
args.name,
|
|
myOptions
|
|
);
|
|
}
|
|
|
|
await fs.unlink(srcPath);
|
|
await tx.commit();
|
|
return newImage;
|
|
} catch (e) {
|
|
await tx.rollback();
|
|
throw e;
|
|
}
|
|
};
|
|
};
|