我已经解决这个问题好几天了,但在 StackExchange 或任何 Google 搜索中都找不到解决方案。
我有以下目录结构(简化了,因为实际上有 20,000 多个文件)。每年每个月都有一个目录。每个月每一天都有一个目录。
2017
01
01
...
31
...
12
2018
2019
这些目录中有图像:
123_43e6d929fcdbfa00aee1892893127b34.png
4567_24a847285bae9ddb6d3c33d237c6d481.jpg
我想要实现的目标
我想为保存在同一目标位置但带有后缀的每个文件创建缩略图版本(调整大小和裁剪)。因此,上述示例将是:
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
我尝试过的方法
for file in *.jpg; do convert $file -set filename:base "%[base]" -resize 120x160^ -gravity center -extent 120x160 -quality 90\> "%[filename:base]-120x160.jpg"; done
我认为这会导致一些循环,因为我们在循环过程中会创建新图像。因此,我创建了一个 PHP 脚本来添加检查,并得出了以下结论:
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]\"");
}
效果很好,但是创建所有 66,000 个缩略图需要很长时间,而且会降低服务器性能。我以为这可能mogrify
是答案,但必须在每个目录中运行,而目录有数千个。
有人可以建议一个解决方案吗-shell,bash,php,任何可以实现这一点的东西?
谢谢
答案1
我认为这会引起一些循环,因为我们在循环过程中创建了新的图像。
不,文件列表是先创建,然后迭代的,因此您不会重新处理您创建的文件。如果您想避免在重新运行脚本时重新处理文件,那就另当别论了:
- 您可以通过使用排除来避免处理拇指否定匹配(或更严格的正匹配),
- 但是,如果缩略图已经存在,你也许还应该有一些逻辑来避免处理文件(我假设源图像从未更新)
...但据我所知,这就是你的 PHP 代码所做的。
效果很好,但是创建全部 66,000 个缩略图会花很长时间,而且会损害服务器性能。
从 66K 文件中生成三个缩略图确实需要一些时间。但无论你做什么,你都必须对每个缩略图进行缩放和重新编码,这会占用一些 CPU。你可以设想避免读取源文件三次,但 Linux 中的文件缓存会处理这个问题。
如果您不想让该进程占用您的服务器,您可以在每个图像(或每个每日目录)后添加 1-2 秒的睡眠命令。
答案2
以下是使用 Make 的示例。这样做的好处是它不会尝试制作您已有的任何东西。因此,您可以安全地运行一段时间然后停止,然后在有时间时再次运行。您还可以提供 -j 标志,例如“make -j 4”将一次使用最多 4 个 CPU 核心(这会更快,但会使用更多资源。)
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 $@
将代码复制到位于树根目录的名为“Makefile”的文件中(注意缩进必须是 TABS!我知道,对吧?)。cd 到该目录并输入“make”(或“make -j 4”等)。为了使其运行得更慢,您可以使用“nice”,如“nice make”