#5576 Reestructurar el directorio de imagenes #1618

Open
alexandre wants to merge 37 commits from 5576-reestructurar-directorio-imagenes into dev
12 changed files with 441 additions and 96 deletions

View File

@ -73,6 +73,9 @@
"ImageCollectionSize": { "ImageCollectionSize": {
"dataSource": "vn" "dataSource": "vn"
}, },
"ImageConfig": {
"dataSource": "vn"
},
"ImageContainer": { "ImageContainer": {
"dataSource": "imageStorage" "dataSource": "imageStorage"
}, },

View File

@ -0,0 +1,27 @@
{
"name": "ImageConfig",
"base": "VnModel",
"options": {
"mysql": {
"table": "hedera.imageConfig"
}
},
"properties": {
"id": {
"type": "number",
"id": true
},
"maxSize": {
"type": "number"
},
"useXsendfile": {
"type": "number"
},
"url": {
"type": "string"
},
"dirLevels": {
"type": "number"
}
}
}

View File

@ -1,24 +1,34 @@
const fs = require('fs-extra'); const fs = require('fs-extra');
const path = require('path'); const path = require('path');
const gm = require('gm'); 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 => { module.exports = Self => {
require('../methods/image/download')(Self); require('../methods/image/download')(Self);
require('../methods/image/upload')(Self); require('../methods/image/upload')(Self);
require('../methods/image/scrub')(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('/');
Self.resize = async function({collectionName, srcFile, fileName, entityId}) { return {name, dstDir};
const models = Self.app.models; };
Self.getCollection = async collectionName => {
const collection = await models.ImageCollection.findOne( const collection = await models.ImageCollection.findOne({
{ fields: ['id', 'maxWidth', 'maxHeight', 'model', 'property'],
fields: [
'id',
'maxWidth',
'maxHeight',
'model',
'property',
],
where: {name: collectionName}, where: {name: collectionName},
include: { include: {
relation: 'sizes', relation: 'sizes',
@ -26,87 +36,159 @@ module.exports = Self => {
fields: ['width', 'height', 'crop'], fields: ['width', 'height', 'crop'],
}, },
}, },
} });
);
// Insert image row return collection;
const imageName = path.parse(fileName).name; };
await models.Image.upsertWithWhere(
{ Self.getCollectionDir = async collectionName => {
name: imageName, const container = await models.ImageContainer.container(collectionName);
collectionFk: collectionName const rootPath = container.client.root;
}, const collectionDir = path.join(rootPath, collectionName);
{ return collectionDir;
name: imageName, };
collectionFk: collectionName,
updated: Date.vnNow() / 1000, 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) => {
Review

Porque se pone un try ... catch si se vuelve a relanzar el error tal cual?

Porque se pone un `try ... catch` si se vuelve a relanzar el error tal cual?
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) {
Review

Porque se pone un try ... catch si se vuelve a relanzar el error tal cual?

Porque se pone un `try ... catch` si se vuelve a relanzar el error tal cual?
throw new Error(e);
}
};
Self.resize = async function({
Review

Porque se pone un try ... catch si se vuelve a relanzar el error tal cual?

Porque se pone un `try ... catch` si se vuelve a relanzar el error tal cual?
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 // Update entity image file name
const model = models[collection.model]; const model = models[collection.model];
if (!model) throw new Error('No matching model found'); if (!model) throw new Error('No matching model found');
const entity = await model.findById(entityId); const entity = await model.findById(entityId);
if (entity) { if (entity)
await entity.updateAttribute( await entity.updateAttribute(collection.property, name);
collection.property,
imageName
);
}
// Resize // Resize
const container = await models.ImageContainer.container( const collectionDir = await Self.getCollectionDir(collectionName);
collectionName
);
const rootPath = container.client.root;
const collectionDir = path.join(rootPath, collectionName);
// To max size // To max size
const {maxWidth, maxHeight} = collection; const _fullSizePath = Self.getFullSizePath(fileName, collectionDir, dstDir);
const fullSizePath = path.join(collectionDir, 'full');
const toFullSizePath = `${fullSizePath}/${fileName}`; const {fullSizePath, toFullSizePath, toFullSizeOriginalPath} = _fullSizePath;
await fs.mkdir(fullSizePath, {recursive: true}); 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) => { await new Promise((resolve, reject) => {
gm(srcFile) gmInstance
.resize(maxWidth, maxHeight, '>') .resize(maxWidth, maxHeight, '>')
.setFormat('png') .setFormat(PNG)
.quality(100) .quality(DEFAULT_QUALITY)
.write(toFullSizePath, function(err) { .write(toFullSizePath + `.${FORMAT}`, function(err, data) {
if (err) reject(err); if (err) reject(err);
if (!err) resolve(); if (!err) resolve(data);
}); });
}); });
await Self.createLink(toFullSizeOriginalPath, toFullSizePath);
// To collection sizes // To collection sizes
for (const size of collection.sizes()) { for (const size of collection.sizes()) {
const {width, height} = size; const [width, height] = parseSize(size);
const sizePath = path.join(collectionDir, `${width}x${height}`); const sizePath = path.join(
const toSizePath = `${sizePath}/${fileName}`; collectionDir,
`${formatWidthHeight(width, height)}/${dstDir}`
);
try {
await fs.mkdir(sizePath, {recursive: true}); await fs.mkdir(sizePath, {recursive: true});
await new Promise((resolve, reject) => { } catch (e) {
const gmInstance = gm(srcFile); throw new Error(e);
}
const toSizePath = `${sizePath}/${fileName}`;
if (+fileWidth < +width && +fileHeight < +height) {
await new Promise((resolve, reject) => {
if (size.crop) { if (size.crop) {
gmInstance gmInstance
.resize(width, height, '^') .resize(width, height, '^')
.gravity('Center') .gravity(DEFAULT_GRAVITY)
.crop(width, height); .crop(width, height).res(function(err, data) {
} if (err) reject(err);
if (!err) resolve(data);
if (!size.crop) gmInstance.resize(width, height, '>'); });
} else gmInstance.resize(width, height, '>');
gmInstance gmInstance
.setFormat('png') .setFormat(PNG)
.quality(100) .quality(DEFAULT_QUALITY)
.write(toSizePath, function(err) { .write(toSizePath + `.${FORMAT}`, function(err, data) {
if (err) reject(err); if (err) reject(err);
if (!err) resolve(); 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);
}
}; };
}; };

View File

@ -0,0 +1,145 @@
const fs = require('fs-extra');
const models = require('vn-loopback/server/server').models;
const LoopBackContext = require('loopback-context');
const nameItem = 'Pallet';
const collectionName = 'user';
let obj = {};
const STORAGE_IMAGE_USER = 'storage/image/user';
const _23_F2 = '23/f2';
const FULL_23_F2 = `full/${_23_F2}`;
fdescribe('loopback model Image', () => {
const userId = 1107;
const activeCtx = {
accessToken: {userId: userId},
http: {
req: {
headers: {origin: 'http://localhost'},
},
},
};
beforeAll(async() => {
const {name: fileName, id: entityId} = await models.Item.findOne({where: {name: nameItem}});
obj = {
collectionName,
srcFile: 'front/core/directives/no-image.png',
fileName,
entityId,
};
// Ruta del directorio y nombre del archivo que buscas
// Llama a la función para mostrar el listado y verificar el archivo
mostrarListadoYVerificar(`${STORAGE_IMAGE_USER}/full`, nameItem);
cleanResources();
// try {
// await fs.unlink(`${STORAGE_IMAGE_USER}/full/${FULL_23_F2}/${nameItem}`);
// await fs.unlink(`${STORAGE_IMAGE_USER}/160x100/${FULL_23_F2}/${nameItem}`);
// await fs.unlink(`${STORAGE_IMAGE_USER}/520x520/${FULL_23_F2}/${nameItem}`);
// await fs.unlink(`${STORAGE_IMAGE_USER}/1600x1600/${FULL_23_F2}/${nameItem}`);
// await fs.unlink(`${STORAGE_IMAGE_USER}/full/${nameItem}`);
// await fs.unlink(`${STORAGE_IMAGE_USER}/160x100/${nameItem}`);
// await fs.unlink(`${STORAGE_IMAGE_USER}/520x520/${nameItem}`);
// await fs.unlink(`${STORAGE_IMAGE_USER}/1600x1600/${nameItem}`);
// } catch (e) {
// console.warn(e);
// }
});
beforeEach(() => {
spyOn(LoopBackContext, 'getCurrentContext').and.returnValue({
active: activeCtx,
});
});
it('should handle folder destination', async() => {
try {
const {name, dstDir} = await models.Image.handleFolderDestination(obj.fileName);
expect(name).toEqual(nameItem);
expect(dstDir).toEqual(`${_23_F2}`);
const collectionDir = await models.Image.getCollectionDir(collectionName);
expect(collectionDir).toEqual(`${STORAGE_IMAGE_USER}`);
} catch (e) {
throw new Error(e);
}
});
it('should handle full size path', async() => {
try {
const {dstDir} = await models.Image.handleFolderDestination(obj.fileName);
const collectionDir = await models.Image.getCollectionDir(collectionName);
const _fullSizePath = models.Image.getFullSizePath(obj.fileName, collectionDir, dstDir);
const {fullSizePath, toFullSizePath, toFullSizeOriginalPath} = _fullSizePath;
expect(fullSizePath).toEqual(`${STORAGE_IMAGE_USER}/${FULL_23_F2}`);
expect(toFullSizePath).toEqual(`${STORAGE_IMAGE_USER}/${FULL_23_F2}/${nameItem}`);
expect(toFullSizeOriginalPath).toEqual(`${STORAGE_IMAGE_USER}/full/${nameItem}`);
} catch (e) {
throw new Error(e);
}
});
it('should resize', async() => {
try {
await models.Image.resize(obj);
const fileExists = await fs.exists(`${STORAGE_IMAGE_USER}/${FULL_23_F2}/${nameItem}.png`);
expect(fileExists).toBeTrue();
const linkExists = await fs.readlink(`${STORAGE_IMAGE_USER}/full/${nameItem}`);
expect(linkExists).toEqual(`${STORAGE_IMAGE_USER}/${FULL_23_F2}/${nameItem}`);
} catch (e) {
throw new Error(e);
}
});
afterAll(async() => {
// cleanResources();
});
});
async function cleanResources() {
try {
await fs.unlink(`${STORAGE_IMAGE_USER}/160x160/${nameItem}`);
// await fs.unlink(`${STORAGE_IMAGE_USER}/160x160/${_23_F2}/${nameItem}`);
await fs.unlink(`${STORAGE_IMAGE_USER}/520x520/${nameItem}`);
// await fs.unlink(`${STORAGE_IMAGE_USER}/520x520/${_23_F2}/${nameItem}`);
await fs.unlink(`${STORAGE_IMAGE_USER}/1600x1600/${nameItem}`);
// await fs.unlink(`${STORAGE_IMAGE_USER}/1600x1600/${_23_F2}/${nameItem}`);
await fs.unlink(`${STORAGE_IMAGE_USER}/full/${nameItem}`);
// await fs.unlink(`${STORAGE_IMAGE_USER}/full/${_23_F2}/${nameItem}`);
} catch (e) {
console.warn(e);
}
}
function mostrarListadoYVerificar(rutaDirectorio, archivoBuscado) {
// Lee el contenido del directorio
fs.readdir(rutaDirectorio, (err, archivos) => {
if (err) {
console.error('Error al leer el directorio:', err);
return;
}
// Muestra el listado de archivos
console.log('Listado de archivos en el directorio:', rutaDirectorio);
archivos.forEach(archivo => {
console.log(archivo);
});
// Verifica si el archivo buscado está en el listado
if (archivos.includes(archivoBuscado))
console.log(`El archivo "${archivoBuscado}" está presente en el directorio.`);
else
console.log(`El archivo "${archivoBuscado}" no está presente en el directorio.`);
});
}

View File

@ -3057,6 +3057,10 @@ INSERT INTO `vn`.`buyConfig` (`id`, `monthsAgo`)
VALUES VALUES
(1, 6); (1, 6);
INSERT INTO `hedera`.`imageConfig` (`id`, `maxSize`, `useXsendfile`, `dirLevels`, `url`)
VALUES
(1, 20, 0, 2, 'marvel.com');
INSERT INTO `vn`.`invoiceInSerial` (`code`, `description`, `cplusTerIdNifFk`, `taxAreaFk`) INSERT INTO `vn`.`invoiceInSerial` (`code`, `description`, `cplusTerIdNifFk`, `taxAreaFk`)
VALUES VALUES
('C', 'Asgard', 1, 'WORLD'), ('C', 'Asgard', 1, 'WORLD'),
@ -3065,9 +3069,7 @@ INSERT INTO `vn`.`invoiceInSerial` (`code`, `description`, `cplusTerIdNifFk`, `t
('W', 'Vanaheim', 1, 'WORLD'); ('W', 'Vanaheim', 1, 'WORLD');
INSERT INTO `hedera`.`imageConfig` (`id`, `maxSize`, `useXsendfile`, `url`)
VALUES
(1, 0, 0, 'marvel.com');
INSERT INTO vn.XDiario (id, ASIEN, FECHA, SUBCTA, CONTRA, CONCEPTO, EURODEBE, EUROHABER, BASEEURO, SERIE, FACTURA, IVA, RECEQUIV, CLAVE, CAMBIO, DEBEME, HABERME, AUXILIAR, MONEDAUSO, TIPOOPE, NFACTICK, TERIDNIF, TERNIF, TERNOM, OPBIENES, L340, enlazado, FECHA_EX, LRECT349, empresa_id, LDIFADUAN, METAL, METALIMP, CLIENTE, METALEJE, FECHA_OP, FACTURAEX, TIPOCLAVE, TIPOEXENCI, TIPONOSUJE, TIPOFACT, TIPORECTIF, SERIE_RT, FACTU_RT, BASEIMP_RT, BASEIMP_RF, RECTIFICA, FECHA_RT, FECREGCON, enlazadoSage) INSERT INTO vn.XDiario (id, ASIEN, FECHA, SUBCTA, CONTRA, CONCEPTO, EURODEBE, EUROHABER, BASEEURO, SERIE, FACTURA, IVA, RECEQUIV, CLAVE, CAMBIO, DEBEME, HABERME, AUXILIAR, MONEDAUSO, TIPOOPE, NFACTICK, TERIDNIF, TERNIF, TERNOM, OPBIENES, L340, enlazado, FECHA_EX, LRECT349, empresa_id, LDIFADUAN, METAL, METALIMP, CLIENTE, METALEJE, FECHA_OP, FACTURAEX, TIPOCLAVE, TIPOEXENCI, TIPONOSUJE, TIPOFACT, TIPORECTIF, SERIE_RT, FACTU_RT, BASEIMP_RT, BASEIMP_RF, RECTIFICA, FECHA_RT, FECREGCON, enlazadoSage)
VALUES VALUES

View File

@ -0,0 +1,4 @@
ALTER TABLE `hedera`.`imageConfig` ADD dirLevels INT UNSIGNED NOT NULL DEFAULT 2;
INSERT INTO `salix`.`ACL` (`model`, `property`, `accessType`, `permission`, `principalType`, `principalId`)
VALUES
('ImageConfig', '*', 'READ', 'ALLOW', 'ROLE', 'employee');

19
storage/image/move.sh Normal file
View File

@ -0,0 +1,19 @@
#!/bin/bash
DIR_LEVELS=2
for collection in */ ; do
for size in "$collection"*/ ; do
for image in "$size"* ; do
if [ -f "$image" ]; then
fileName=$(basename "$image")
imageName="${fileName%.*}"
hash=$(echo -n "$imageName" | sha1sum | awk '{print $1}')
first=$(echo "$hash" | cut -c"$inicio"-"$DIR_LEVELS")
second=$(echo "$hash" | cut -c"$DIR_LEVELS"-"$fin")
path=$(dirname "$image")/${first}/${second}
mkdir -p $path
ln -s "$image" "$fileName"
mv $image $path/$fileName
fi
done
done
done

28
storage/image/prod.sh Normal file
View File

@ -0,0 +1,28 @@
#!/bin/bash
MIN_DIR_LEVELS=0
DIR_LEVELS=2
START=1
END=3
# Directorio que contiene las carpetas con las fotos
MAIN_DIR=$1
# Iterar a través de cada carpeta en el directorio principal
for image in "$MAIN_DIR"/*; do
# Verificar si es un directorio
# Iterar a través de cada imagen en la subcarpeta
# Verificar si es un archivo
if [ -f "$image" ]; then
# Obtener el nombre de la imagen
fileName=$(basename "$image")
imageName="${fileName%.*}"
hash=$(echo -n "$imageName" | sha1sum | awk '{print $1}')
first=$(echo "$hash" | cut -c"$START"-"$DIR_LEVELS")
second=$(echo "$hash" | cut -c"$DIR_LEVELS"-"$END")
path=$(dirname "$image")/${first}/${second}
mkdir -p $path
# Crear un enlace simbólico en la carpeta principal
mv $image $path/$fileName
fi
# done
# fi
done

View File

@ -0,0 +1,13 @@
#!/bin/bash
for collection in */ ; do
for size in "$collection"*/ ; do
for image in "$size"* ; do
fileName=$(basename "$image")
imageName="${fileName%.*}"
hash=$(echo -n "$imageName" | sha1sum | awk '{print $1}')
mkdir -p $(dirname "$image")/${hash:2:2}/${hash:0:2}
ln -s $image $(dirname "$image")/${hash:2:2}/${hash:0:2}/$fileName
done
done
done

22
storage/image/script.sh Normal file
View File

@ -0,0 +1,22 @@
#!/bin/bash
# Directorio que contiene las carpetas con las fotos
directorio_principal=$1
# Iterar a través de cada carpeta en el directorio principal
for carpeta in "$directorio_principal"/*; do
# Verificar si es un directorio
if [ -d "$carpeta" ]; then
# Iterar a través de cada imagen en la subcarpeta
for imagen in "$carpeta"/*.png; do
# Verificar si es un archivo
if [ -f "$imagen" ]; then
# Obtener el nombre de la imagen
nombre_imagen=$(basename "$imagen")
# Crear un enlace simbólico en la carpeta principal
ln -s "$imagen" "$1/_$nombre_imagen"
fi
done
fi
done

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 KiB