195 lines
6.9 KiB
JavaScript
195 lines
6.9 KiB
JavaScript
const fs = require('fs-extra');
|
|
const path = require('path');
|
|
const gm = require('gm');
|
|
const crypto = require('crypto');
|
|
const {models} = require('vn-loopback/server/server');
|
|
const PNG = 'png';
|
|
const FORMAT = PNG;
|
|
const DEFAULT_GRAVITY = 'Center';
|
|
const DEFAULT_QUALITY = 100;
|
|
const SIZE_UNIT = 'x';
|
|
const formatWidthHeight = (width, height) => `${width}${SIZE_UNIT}${height}`;
|
|
const parseSize = value => formatWidthHeight(value.width, value.height).split(SIZE_UNIT);
|
|
module.exports = Self => {
|
|
require('../methods/image/download')(Self);
|
|
require('../methods/image/upload')(Self);
|
|
require('../methods/image/scrub')(Self);
|
|
Self.handleFolderDestination = async fileName => {
|
|
// Insert image row
|
|
const {name} = path.parse(fileName);
|
|
const hash = crypto.createHash('sha1').update(name).digest('hex');
|
|
const pairs = hash.match(/(..?)/g);
|
|
const {dirLevels} = await models.ImageConfig.findOne({
|
|
fields: ['dirLevels'],
|
|
});
|
|
const dstDir = pairs.slice(0, dirLevels).reverse().join('/');
|
|
|
|
return {name, dstDir};
|
|
};
|
|
Self.getCollection = async collectionName => {
|
|
const collection = await models.ImageCollection.findOne({
|
|
fields: ['id', 'maxWidth', 'maxHeight', 'model', 'property'],
|
|
where: {name: collectionName},
|
|
include: {
|
|
relation: 'sizes',
|
|
scope: {
|
|
fields: ['width', 'height', 'crop'],
|
|
},
|
|
},
|
|
});
|
|
|
|
return collection;
|
|
};
|
|
|
|
Self.getCollectionDir = async collectionName => {
|
|
const container = await models.ImageContainer.container(collectionName);
|
|
const rootPath = container.client.root;
|
|
const collectionDir = path.join(rootPath, collectionName);
|
|
return collectionDir;
|
|
};
|
|
|
|
Self.getFullSizePath = (fileName, collectionDir, dstDir) => {
|
|
try {
|
|
const fullSizePath = path.join(collectionDir, `full/${dstDir}`);
|
|
const fullSizeOriginalPath = path.join(collectionDir, `full`);
|
|
const toFullSizePath = `${fullSizePath}/${fileName}`;
|
|
const toFullSizeOriginalPath = `${fullSizeOriginalPath}/${fileName}`;
|
|
return {
|
|
fullSizePath,
|
|
toFullSizePath,
|
|
toFullSizeOriginalPath
|
|
};
|
|
} catch (e) {
|
|
throw new Error(e);
|
|
}
|
|
};
|
|
Self.removeLink = async(child, parent = null) => {
|
|
try {
|
|
await fs.unlink(child);
|
|
await fs.symlink(parent, child);
|
|
} catch (e) {
|
|
throw new Error(e);
|
|
}
|
|
};
|
|
Self.createLink = async(parent, child = null) => {
|
|
try {
|
|
const exists = await fs.exists(parent);
|
|
exists && await fs.unlink(parent);
|
|
await fs.symlink(child, parent);
|
|
} catch (e) {
|
|
throw new Error(e);
|
|
}
|
|
};
|
|
|
|
Self.resize = async function({
|
|
collectionName,
|
|
srcFile,
|
|
fileName,
|
|
entityId,
|
|
}) {
|
|
const {name, dstDir} = await Self.handleFolderDestination(fileName);
|
|
try {
|
|
const baseUpsert = {
|
|
name,
|
|
collectionFk: collectionName,
|
|
};
|
|
await models.Image.upsertWithWhere(baseUpsert,
|
|
{
|
|
...baseUpsert,
|
|
updated: Date.vnNow() / 1000,
|
|
});
|
|
|
|
const collection = await Self.getCollection(collectionName);
|
|
const {maxWidth, maxHeight} = collection;
|
|
|
|
// Update entity image file name
|
|
const model = models[collection.model];
|
|
if (!model) throw new Error('No matching model found');
|
|
|
|
const entity = await model.findById(entityId);
|
|
if (entity)
|
|
await entity.updateAttribute(collection.property, name);
|
|
|
|
// Resize
|
|
const collectionDir = await Self.getCollectionDir(collectionName);
|
|
|
|
// To max size
|
|
const _fullSizePath = Self.getFullSizePath(fileName, collectionDir, dstDir);
|
|
|
|
const {fullSizePath, toFullSizePath, toFullSizeOriginalPath} = _fullSizePath;
|
|
|
|
await fs.mkdir(fullSizePath, {recursive: true});
|
|
|
|
const gmInstance = gm(srcFile);
|
|
let fileWidth = null;
|
|
let fileHeight = null;
|
|
|
|
gmInstance.size(function(err, size) {
|
|
if (err) throw new Error(err);
|
|
[fileWidth, fileHeight] = parseSize(size);
|
|
});
|
|
await new Promise((resolve, reject) => {
|
|
gmInstance
|
|
.resize(maxWidth, maxHeight, '>')
|
|
.setFormat(PNG)
|
|
.quality(DEFAULT_QUALITY)
|
|
.write(toFullSizePath + `.${FORMAT}`, function(err, data) {
|
|
if (err) reject(err);
|
|
if (!err) resolve(data);
|
|
});
|
|
});
|
|
|
|
await Self.createLink(toFullSizeOriginalPath, toFullSizePath);
|
|
// To collection sizes
|
|
for (const size of collection.sizes()) {
|
|
const [width, height] = parseSize(size);
|
|
|
|
const sizePath = path.join(
|
|
collectionDir,
|
|
`${formatWidthHeight(width, height)}/${dstDir}`
|
|
);
|
|
|
|
try {
|
|
await fs.mkdir(sizePath, {recursive: true});
|
|
} catch (e) {
|
|
throw new Error(e);
|
|
}
|
|
|
|
const toSizePath = `${sizePath}/${fileName}`;
|
|
if (+fileWidth < +width && +fileHeight < +height) {
|
|
await new Promise((resolve, reject) => {
|
|
if (size.crop) {
|
|
gmInstance
|
|
.resize(width, height, '^')
|
|
.gravity(DEFAULT_GRAVITY)
|
|
.crop(width, height).res(function(err, data) {
|
|
if (err) reject(err);
|
|
if (!err) resolve(data);
|
|
});
|
|
} else gmInstance.resize(width, height, '>');
|
|
|
|
gmInstance
|
|
.setFormat(PNG)
|
|
.quality(DEFAULT_QUALITY)
|
|
.write(toSizePath + `.${FORMAT}`, function(err, data) {
|
|
if (err) reject(err);
|
|
if (!err) resolve(data);
|
|
});
|
|
});
|
|
}
|
|
|
|
const sizeOriginalPath = path.join(
|
|
collectionDir,
|
|
formatWidthHeight(width, height)
|
|
);
|
|
|
|
const toSizeOriginalPath = `${sizeOriginalPath}/${fileName}`;
|
|
|
|
await Self.createLink(toSizeOriginalPath, toSizePath);
|
|
}
|
|
} catch (e) {
|
|
throw new Error(e);
|
|
}
|
|
};
|
|
};
|