From 3a1520cfea5ef053ea2e9458353f1dc3a27b30d8 Mon Sep 17 00:00:00 2001 From: Javier Segarra Date: Mon, 20 Nov 2023 14:56:01 +0100 Subject: [PATCH] refs #5576 feat: add symbolLink in folder when original is bigger than folder --- back/models/image.js | 282 ++++++++++++++++++++++++++++--------------- 1 file changed, 186 insertions(+), 96 deletions(-) diff --git a/back/models/image.js b/back/models/image.js index 58e26a7348..f7fb08bfae 100644 --- a/back/models/image.js +++ b/back/models/image.js @@ -2,132 +2,222 @@ 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.resize = async function({collectionName, srcFile, fileName, entityId}) { - const models = Self.app.models; - - const collection = await models.ImageCollection.findOne( - { - fields: [ - 'id', - 'maxWidth', - 'maxHeight', - 'model', - 'property', - ], - where: {name: collectionName}, - include: { - relation: 'sizes', - scope: { - fields: ['width', 'height', 'crop'], - }, - }, - } - ); - + Self.handleFolderDestination = async fileName => { // Insert image row - const imageName = path.parse(fileName).name; - const shasum = crypto.createHash('sha1'); - shasum.update(imageName); - const hash = shasum.digest('hex'); + const {name} = path.parse(fileName); + const hash = crypto.createHash('sha1').update(name).digest('hex'); const pairs = hash.match(/(..?)/g); - const imageConfig = await models.ImageConfig.findOne({fields: ['dirLevels']}); - const firstPairs = pairs.slice(0, imageConfig.dirLevels).reverse(); - const dstDir = firstPairs.join('/'); + const {dirLevels} = await models.ImageConfig.findOne({ + fields: ['dirLevels'], + }); + const dstDir = pairs.slice(0, dirLevels).reverse().join('/'); - await models.Image.upsertWithWhere( - { - name: imageName, - collectionFk: collectionName + 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) => { + 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 + }; + }; + Self.removeLink = async(child, parent = null) => { + try { + await fs.unlink(child); + } catch (e) { + throw new Error(e); + } + try { + await fs.symlink(parent, child); + } catch (e) { + throw new Error(e); + } + }; + Self.createLink = async(parent, child = null) => { + try { + const exists = await fs.exists(parent); + if (exists) + await fs.unlink(parent); + } catch (e) { + throw new Error(e); + } + try { + await fs.symlink(child, parent); + const link = await fs.readlink(parent); + console.log(link); + } catch (e) { + throw new Error(e); + } + }; + + Self.resize = async function({ + collectionName, + srcFile, + fileName, + entityId, + }) { + const {name, dstDir} = await Self.handleFolderDestination(fileName); + try { + await models.Image.upsertWithWhere({ + name, + collectionFk: collectionName, }, { - name: imageName, + name, collectionFk: collectionName, updated: Date.vnNow() / 1000, - } - ); + }); + } catch (e) { + debugger; + throw new Error(e); + } + 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, - imageName - ); - } + if (entity) + await entity.updateAttribute(collection.property, name); // Resize - const container = await models.ImageContainer.container( - collectionName - ); - const rootPath = container.client.root; - const collectionDir = path.join(rootPath, collectionName); + const collectionDir = await Self.getCollectionDir(collectionName); // To max size - const {maxWidth, maxHeight} = collection; - const fullSizePath = path.join(collectionDir, `full/${dstDir}`); - const fullSizeOriginalPath = path.join(collectionDir, `full`); - const toFullSizePath = `${fullSizePath}/${fileName}`; - const toFullSizeOriginalPath = `${fullSizeOriginalPath}/${fileName}`; + const _fullSizePath = Self.getFullSizePath(fileName, collectionDir, dstDir); - await fs.mkdir(fullSizePath, {recursive: true}); - await new Promise((resolve, reject) => { - gm(srcFile) - .resize(maxWidth, maxHeight, '>') - .setFormat('png') - .quality(100) - .write(toFullSizePath, function(err) { - if (err) reject(err); - if (!err) resolve(); - }); + const {fullSizePath, toFullSizePath, toFullSizeOriginalPath} = _fullSizePath; + try { + await fs.mkdir(fullSizePath, {recursive: true}); + } catch (e) { + debugger; + throw new Error(e); + } + const gmInstance = gm(srcFile); + let fileWidth = null; + let fileHeight = null; + + gmInstance.size(function(err, size) { + if (err) console.error(err); + [fileWidth, fileHeight] = parseSize(size); }); try { - await fs.unlink(toFullSizeOriginalPath); - } catch (e) {} - await fs.symlink(toFullSizeOriginalPath, toFullSizePath, 'file'); - - // To collection sizes - for (const size of collection.sizes()) { - const {width, height} = size; - - const sizePath = path.join(collectionDir, `${width}x${height}/${dstDir}`); - const toSizePath = `${sizePath}/${fileName}`; - const sizeOriginalPath = path.join(collectionDir, `${width}x${height}`); - const toSizeOriginalPath = `${sizeOriginalPath}/${fileName}`; - - await fs.mkdir(sizePath, {recursive: true}); await new Promise((resolve, reject) => { - const gmInstance = gm(srcFile); - - if (size.crop) { - gmInstance - .resize(width, height, '^') - .gravity('Center') - .crop(width, height); - } - - if (!size.crop) gmInstance.resize(width, height, '>'); - gmInstance - .setFormat('png') - .quality(100) - .write(toSizePath, function(err) { + .resize(maxWidth, maxHeight, '>') + .setFormat(PNG) + .quality(DEFAULT_QUALITY) + .write(toFullSizePath + `.${FORMAT}`, function(err, data) { if (err) reject(err); - if (!err) resolve(); + if (!err) resolve(data); }); }); + } catch (e) { + debugger; + throw new Error(e); + } + + await Self.createLink(toFullSizeOriginalPath, toFullSizePath); + /* try { + await fs.unlink(toFullSizeOriginalPath); + } catch (e) { + debugger; + throw new Error(e); + } + try { + await fs.symlink(toFullSizeOriginalPath, toFullSizePath, 'file'); + } catch (e) { + debugger; + throw new Error(e); + }*/ + // 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.unlink(toSizeOriginalPath); - } catch (e) {} - await fs.symlink(toSizePath, toSizeOriginalPath); + await fs.mkdir(sizePath, {recursive: true}); + } catch (e) { + debugger; + 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}`; + + // if (fileWidth > width && fileHeight < height) + await Self.createLink(toSizeOriginalPath, toSizePath); } }; };