Crear miniaturas de varios tamaños con sufijo de nombre de archivo

Crear miniaturas de varios tamaños con sufijo de nombre de archivo

He estado luchando con este problema durante un par de días y no he logrado encontrar una solución en StackExchange ni en ninguna búsqueda en Google.

Tengo la siguiente estructura de directorios (simplificada porque en realidad hay más de 20.000 archivos). Cada año tiene un directorio para cada mes. Cada mes tiene un directorio para cada día.

2017
  01
    01
    ...
    31
  ...
  12
2018
2019

Dentro de estos directorios hay imágenes:

123_43e6d929fcdbfa00aee1892893127b34.png
4567_24a847285bae9ddb6d3c33d237c6d481.jpg

lo que quiero lograr

Me gustaría crear versiones en miniatura (redimensionadas y recortadas) de cada archivo guardado en el mismo destino pero con sufijos. Entonces el ejemplo anterior sería:

123_43e6d929fcdbfa00aee1892893127b34.png
123_43e6d929fcdbfa00aee1892893127b34-120x160.png
123_43e6d929fcdbfa00aee1892893127b34-300x300.png
123_43e6d929fcdbfa00aee1892893127b34-800x800.png
4567_24a847285bae9ddb6d3c33d237c6d481.jpg
4567_24a847285bae9ddb6d3c33d237c6d481-120x160.jpg
4567_24a847285bae9ddb6d3c33d237c6d481-300x300.jpg
4567_24a847285bae9ddb6d3c33d237c6d481-800x800.jpg

lo que he probado

for file in *.jpg; do convert $file -set filename:base "%[base]" -resize 120x160^ -gravity center -extent 120x160 -quality 90\> "%[filename:base]-120x160.jpg"; done

Creo que esto causa un pequeño bucle porque estamos creando nuevas imágenes a medida que avanzamos. Entonces creé un script PHP para agregar una verificación y se me ocurrió esto:

foreach ($all_files as $file) {
    $path = $file->getPathname();

    $resized = str_replace_last('.', '-120x160.', $path);             
    if (preg_match('/\-\d{3}x\d{3}\./', $path) || file_exists($resized) || substr($path,-4) != '.jpg') continue;

    exec("convert \"$path\" -set filename:base \"%d/%[base]-120x160.jpg\" -resize 120x160^ -gravity center -extent 120x160 -quality 95\> \"%[filename:base]\";
          convert \"$path\" -set filename:base \"%d/%[base]-300x300.jpg\" -resize 300x300^ -gravity center -extent 300x300 -quality 95\> \"%[filename:base]\";
          convert \"$path\" -set filename:base \"%d/%[base]-800x800.jpg\" -resize 800x800^ -gravity center -extent 800x800 -quality 95\> \"%[filename:base]\"");
}

Esto funciona muy bien, sin embargo, tomará una eternidad crear las 66,000 miniaturas y reducir el rendimiento del servidor. Pensé que tal vez mogrifysería la respuesta, pero debe ejecutarse en cada directorio y hay miles de directorios.

¿Alguien podría sugerir una solución: shell, bash, php, cualquier cosa que pueda lograr esto?

Gracias

Respuesta1

Creo que esto causa un pequeño bucle porque estamos creando nuevas imágenes a medida que avanzamos.

No, la lista de archivos se crea y luego se itera, por lo que no reprocesará los archivos que cree. Si desea evitar reprocesar el archivo si vuelve a ejecutar el script, es otra cuestión:

  • Puede evitar procesar los pulgares excluyéndolos con uncoincidencia negativa(o una coincidencia positiva más estricta),
  • Pero quizás también deberías tener cierta lógica para evitar procesar un archivo si los pulgares ya están allí (supongo que la imagen de origen nunca se actualiza)

... pero hasta donde puedo decir, esto es lo que hace el código PHP.

Esto funciona muy bien, sin embargo, tomará una eternidad crear las 66,000 miniaturas y reducir el rendimiento del servidor.

De hecho, producir tres pulgares a partir de archivos de 66K llevará algún tiempo. Pero hagas lo que hagas, tienes que realizar el escalado y la recodificación para cada pulgar y esto es lo que requerirá algo de CPU. Podría imaginarse evitar leer el archivo fuente tres veces, pero el almacenamiento en caché de archivos en Linux se encargará de eso.

Si no desea que el proceso acapare su servidor, puede agregar un comando de suspensión de 1 a 2 segundos después de cada imagen (o cada directorio diario).

Respuesta2

Aquí hay un ejemplo usando Make. La ventaja aquí es que no intentará crear nada que ya tengas. Por lo tanto, es seguro ejecutarlo por un momento y luego detenerlo, luego ejecutarlo nuevamente más tarde cuando tenga tiempo. También puede indicar el indicador -j, por ejemplo, "make -j 4" utilizará hasta 4 núcleos de CPU a la vez (lo que será más rápido, pero utilizará más recursos).

ifeq (${MAKELEVEL}, 0)
   # path to this Makefile
   mfile = Makefile
endif

subdirs = $(shell find * -type d -print -prune)
files = $(wildcard *.jpg) $(wildcard *.png)
thumbs = $(wildcard *-120x160.*) $(wildcard *-300x300.*) $(wildcard *-800x800.*)
notthumbs = ${basename $(filter-out $(thumbs), $(files) ) }
neededthumbs = $(foreach f, ${notthumbs}, ${f}-120x160.jpg ${f}-300x300.jpg ${f}-800x800.jpg)

all: thumbs recurse

thumbs: ${neededthumbs}

recurse:
        $(foreach f, ${subdirs}, $(MAKE) -C ${f} -f ../${mfile} mfile=../${mfile} all ; )

%-120x160.jpg: %.jpg
        echo convert $< -resize 120x160 $@
%-300x300.jpg: %.jpg
        echo convert $< -resize 300x300 $@
%-800x800.jpg: %.jpg
        echo convert $< -resize 800x800 $@
%-120x160.jpg: %.png
        echo convert $< -resize 120x160 $@
%-300x300.jpg: %.png
        echo convert $< -resize 300x300 $@
%-800x800.jpg: %.png
        echo convert $< -resize 800x800 $@

Copie el código en un archivo (tenga en cuenta que las sangrías DEBEN ser PESTAÑAS. Lo sé, ¿verdad?) llamado "Makefile" en la raíz de su árbol. cd a ese directorio y escriba "make" (o "make -j 4", etc.) Para que se ejecute más lento, puede usar "nice" como en "nice make"

información relacionada