#5576 Reestructurar el directorio de imagenes #1618
|
@ -73,6 +73,9 @@
|
|||
"ImageCollectionSize": {
|
||||
"dataSource": "vn"
|
||||
},
|
||||
"ImageConfig": {
|
||||
"dataSource": "vn"
|
||||
},
|
||||
"ImageContainer": {
|
||||
"dataSource": "imageStorage"
|
||||
},
|
||||
|
|
|
@ -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"
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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) => {
|
||||
|
||||
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) {
|
||||
juan
commented
Porque se pone un Porque se pone un `try ... catch` si se vuelve a relanzar el error tal cual?
|
||||
throw new Error(e);
|
||||
}
|
||||
};
|
||||
|
||||
Self.resize = async function({
|
||||
juan
commented
Porque se pone un 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);
|
||||
}
|
||||
};
|
||||
};
|
||||
|
|
|
@ -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.`);
|
||||
});
|
||||
}
|
|
@ -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
|
||||
|
|
|
@ -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');
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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 |
Loading…
Reference in New Issue
Porque se pone un
try ... catch
si se vuelve a relanzar el error tal cual?