#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

2
.gitignore vendored
View File

@ -10,4 +10,4 @@ print.*.json
db.json
junit.xml
.DS_Store
storage
storage

View File

@ -73,6 +73,9 @@
"ImageCollectionSize": {
"dataSource": "vn"
},
"ImageConfig": {
"dataSource": "vn"
},
"ImageContainer": {
"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,112 +1,194 @@
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;
await models.Image.upsertWithWhere(
{
name: imageName,
collectionFk: collectionName
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'],
},
},
{
name: imageName,
collectionFk: collectionName,
updated: Date.vnNow() / 1000,
}
);
// 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
);
}
// Resize
const container = await models.ImageContainer.container(
collectionName
);
const rootPath = container.client.root;
const collectionDir = path.join(rootPath, collectionName);
// To max size
const {maxWidth, maxHeight} = collection;
const fullSizePath = path.join(collectionDir, 'full');
const toFullSizePath = `${fullSizePath}/${fileName}`;
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();
});
});
// To collection sizes
for (const size of collection.sizes()) {
const {width, height} = size;
return collection;
};
const sizePath = path.join(collectionDir, `${width}x${height}`);
const toSizePath = `${sizePath}/${fileName}`;
Self.getCollectionDir = async collectionName => {
const container = await models.ImageContainer.container(collectionName);
const rootPath = container.client.root;
const collectionDir = path.join(rootPath, collectionName);
return collectionDir;
};
await fs.mkdir(sizePath, {recursive: true});
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
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) => {
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);
});
});
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);
}
};
};

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
(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`)
VALUES
('C', 'Asgard', 1, 'WORLD'),
@ -3065,9 +3069,7 @@ INSERT INTO `vn`.`invoiceInSerial` (`code`, `description`, `cplusTerIdNifFk`, `t
('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)
VALUES
@ -3867,4 +3869,4 @@ INSERT INTO vn.sectorCollection
INSERT INTO vn.sectorCollectionSaleGroup
SET id = 9,
sectorCollectionFk = 3,
saleGroupFk = 6;
saleGroupFk = 6;

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