Очистка upload по cron

скрипт удаляющий ненужные файлы и пустые папки

#1 20 сентября 2019 в 10:49
Хай ол,
я тут себе очиститель папки upload замутил по хрону
скрипт cron_cleaner.pl.php:

Конечно ща первый вопрос "почему perl?"...
— Да по приколу :)
— В Перле есть шикарная возможность работы с хешами на собственной базе(снизит нагрузку на память)
— Он(perl) собстна разрабатывался для обработки текстов, а мы именно этим и будем заниматься сравнивая базы
второй вопрос "почему cron?"
— Да не желаю я раз в неделю лазить в админку и жмякать кнопку unfinder(искреннее спасибо Ris — вещь нужная)
всеравно не ведая чё тама удаляется
третий вопрос: "Чё за странное название скипт.php? када он на перле"
— Всё просто в папке upload запрещено правилами apache/nginx отдача php файлов, вот и шифруемся :)

ну и сам скрипт:
  1. #!/usr/bin/perl -w
  2. $| = 1;
  3.  
  4. ###################################################################
  5. #
  6. # Copyright (c) Fazer
  7. # cron_cleaner.pl
  8. # Version: 7.59 (2019-10-15)
  9. # Адаптированный под компонент "Очистка"
  10. #
  11. ###################################################################
  12.  
  13. use strict; # Возможно потребуется доустановить пакеты
  14. use DBI; # yum install perl-DBI perl-DBD-MySQL perl-DB_File
  15. # or
  16. # apt-get install libdbi-perl libdbd-mysql-perl
  17.  
  18. #######################################################################
  19. # Конфигурация (множественные параметры - через пробел/запятую )
  20. #
  21. # что бы реально удалять и переносить - поменять на: my $disabletest = 1;
  22. my $disabletest = 0;
  23. # my $disabletest = 1;
  24.  
  25. # расширения файлов которые будут обрабатываться и удаляться при необходимости
  26. my $extension = "jpeg, jpg, gif, png, bmp";
  27.  
  28. # будет искать во всех ниже перечисленных полях всех таблиц в которых такие поля имеются
  29. my $fields = "photo photos image images img wm_image avatar content html cover_image logo picture";
  30.  
  31. # папки которые сканируем "внутри" ./upload/, например: "000/u1 004 images";
  32. # если оставить пустым ($folders = "";), будет сканироваться вся ./upload/ исключая ./upload/deleted/
  33. my $folders = "000";
  34.  
  35. # Список папок, исключенных из сканирования, ТОЛЬКО "ВЕРХНЕГО" уровня ./upload/NotDelete
  36. # например: "004, default" - не будет сканировать ./upload/004/ и ./upload/default/
  37. my $exceptions = "";
  38.  
  39. # если установить в 'true' ненужные файлы будут удаляться безвозвратно а не переноситься в deleted
  40. my $delete = 'false';
  41.  
  42. # расположение папки конфигурационных файлов CMS относительно самого скрипта
  43. my $conf = '../system/config/';
  44.  
  45. #######################################################################
  46. # Инициализация
  47. #
  48. $ENV{'PATH'} = '/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin';
  49.  
  50. if (@ARGV){
  51. if ($ARGV[0] && $ARGV[0] == 1){$disabletest = 1;}
  52. if ($ARGV[1]){$extension = $ARGV[1];}
  53. if ($ARGV[2]){$fields = $ARGV[2];}
  54. if ($#ARGV > 2){$folders = $ARGV[3];}
  55. if ($ARGV[4]){$exceptions = $ARGV[4];}
  56. }
  57.  
  58. use DB_File;
  59. use File::Basename;
  60. use File::Find qw(finddepth);
  61. use File::Path qw(make_path);
  62. use Cwd;
  63.  
  64. my (%db_connect, %result, @result, $str);
  65. my $prr = "No errors";
  66. my $fullpath = dirname(__FILE__);
  67.  
  68. my $dblock = tie %result, 'DB_File', "$fullpath/result.db", O_RDWR|O_CREAT, 0660, $DB_BTREE or die "Can't tie $fullpath/result.db: $!";
  69. my $fd = $dblock->fd;
  70. open(FDB, "+<&=$fd") || die "Duplicate $!";
  71. unless (flock (FDB, 2|4)) {print "Resource temporarily blocked by previous copy of the script\n";close(FDB);exit;}
  72.  
  73. if ($disabletest){print "TestMode = 'OFF'\n";} else {print "TestMode = 'ON'\n";}
  74.  
  75. my ($n, $h, $d, $m, $y) = (localtime)[1,2,3,4,5];
  76. my $date = sprintf ("The start date is %04d.%02d.%02d %02d:%02d \n==================================\n", $y+1900, $m+1, $d, $h, $n);
  77. print $date;
  78.  
  79. open (ERR, ">$fullpath/cleaner_errors.txt"); print (ERR $date);
  80. print (ERR "The script was running at those parameters:\n");
  81. print (ERR "\$disabletest\t= $disabletest\n\$extension\t= $extension\n\$fields\t\t= $fields\n");
  82. print (ERR "\$folders\t= $folders\n\$exceptions\t= $exceptions\n\$delete\t\t= $delete\n==================================\n");
  83.  
  84. if (open (LOGLINKS,"$fullpath/cleaner_select.txt")){
  85. close (LOGLINKS);
  86. unlink ("$fullpath/cleaner_dirs.txt");
  87. unlink ("$fullpath/cleaner_files.txt");
  88. unlink ("$fullpath/cleaner_select.txt");
  89. }
  90.  
  91. chdir $fullpath or $prr='', print (ERR "Cant change $fullpath $!\n");
  92. $fullpath = getcwd;
  93. # print "Am here $fullpath \n";
  94.  
  95. $folders =~ s/^(.\/|\/)//g;
  96. $folders =~ s/[\s|\r|\n|,]+/ /g;
  97. $extension =~ s/[\s|\r|\n|,]+/ /g;
  98. $fields =~ s/[\s|\r|\n|,]+/ /g;
  99. $exceptions =~ s/[\s|\r|\n|,]+/ /g;
  100.  
  101. my @folders = split (/ /,$folders);
  102. unless (@folders){
  103. my @exceptions = split (/ /,$exceptions);
  104. my $exceptions = 'deleted';
  105. if (@exceptions){
  106. foreach my $exc (@exceptions) {
  107. $exc=~s/\s//g;
  108. if ($exc){$exceptions .= "|".$exc;}
  109. }
  110. }
  111. # print "Exceptions - $exceptions\n";
  112.  
  113. opendir(DIR, "./") or $prr='', print (ERR "Cant open $fullpath $!\n");
  114. my @dirs = grep { (!/^\./) && -d "./$_" } readdir(DIR);
  115. foreach my $dir (@dirs) {
  116. unless ($dir =~ m/^($exceptions)$/i){
  117. push (@folders ,$dir);
  118. }
  119. }
  120. closedir (DIR);
  121. }
  122. # print "Folders - @folders\n";
  123.  
  124. my @extension = split (/ /,$extension);
  125. if (@extension){
  126. $extension = shift (@extension);
  127. if (@extension){
  128. foreach my $exp (@extension) {
  129. $exp=~s/\s//g;
  130. if ($exp){$extension .= "|".$exp;}
  131. }
  132. }
  133. } else {
  134. print (ERR " Nothing to do!,\n no valid extensions\n");
  135. flock(FDB, 8); close FDB;
  136. unlink ("$fullpath/result.db");
  137. print " Nothing to do!,\n no valid extensions\n";
  138. exit;
  139. }
  140. # print "Extensions - $extension\n";
  141.  
  142. my @fields = split (/ /,$fields);
  143. if (@fields){
  144. $fields = "'".shift (@fields)."'";
  145. if (@fields){
  146. foreach my $fi (@fields) {
  147. $fi=~s/\s//g;
  148. if ($fi){$fields .= ", '".$fi."'";}
  149. }
  150. }
  151. } else {
  152. print (ERR " Nothing to do!,\n no valid fields\n");
  153. flock(FDB, 8); close FDB;
  154. unlink ("$fullpath/result.db");
  155. print " Nothing to do!,\n no valid fields\n";
  156. exit;
  157. }
  158. # print "Fields - $fields\n";
  159. # print "ModeTest - $disabletest\n";
  160.  
  161. #######################################################################
  162. # Обработка конфигурационных файлов
  163. #
  164. $conf =~ s/\/$//;
  165.  
  166. open (CONF, "$conf/config.php") or (print (ERR "Can't open $conf at $fullpath: $!")),
  167. unlink ("$fullpath/result.db"), flock(FDB, 8), close FDB, print "Can't open $conf at $fullpath: $!", exit;
  168. while (<CONF>){
  169. $str = $_;
  170. if ($str =~ m/.*'db_(.*)'.*=>.*'(.*)'/i){
  171. $db_connect{$1} = $2;
  172. }
  173. }
  174. close (CONF);
  175.  
  176. # while(($str,)=(each %db_connect)){
  177. # print "$str = $db_connect{$str}\n";
  178. # }
  179.  
  180. # Обработка theme_*.yml файлов на наличие адресов картинок
  181. opendir(DIR, "$conf") or $prr='', (print ERR "Cant open dir $conf at $fullpath $!\n");
  182. my @files = readdir DIR;
  183. closedir (DIR);
  184. foreach my $fileyml (@files) {
  185. if ($fileyml=~/.*\.yml$/i) {
  186. open (FILE, "$conf/$fileyml") or $prr='', print (ERR "Cant open $conf/$fileyml $!\n");
  187. # print "$conf/$fileyml\n";
  188. while (<FILE>){
  189. $str = $_;
  190. my @str = split (/ |\r|\n/,$str);
  191. foreach $str (@str) {
  192. if ($str =~ m/(.*\/.*\.($extension)$)/i){
  193. $result{$1} = undef;
  194. # print "$1 \n";
  195. }
  196. }
  197. }
  198. close (FILE);
  199. }
  200. }
  201.  
  202. #######################################################################
  203. # Запрос к базе, поиск всех нужных полей
  204. #
  205. my $db = DBI->connect("DBI:mysql:$db_connect{'base'}:$db_connect{'host'}", "$db_connect{'user'}",
  206. "$db_connect{'pass'}", {mysql_auto_reconnect=>1})
  207. or (print (ERR "Error connecting to database: $!")),
  208. unlink ("$fullpath/result.db"), flock(FDB, 8), close FDB, print "Error connecting to database: $!", exit;
  209.  
  210. my $request = "SELECT DISTINCT TABLE_NAME,COLUMN_NAME FROM INFORMATION_SCHEMA.COLUMNS
  211. WHERE COLUMN_NAME IN ($fields) AND TABLE_SCHEMA='$db_connect{'base'}';";
  212.  
  213. my $answer = $db->prepare($request);
  214. $answer->execute;
  215. while (my @row = $answer->fetchrow_array) {
  216. push @result, [ $row[0], $row[1] ];
  217. }
  218. $answer->finish;
  219.  
  220.  
  221.  
  222. ###############################################################
  223. # Запрос к базе, создание хеша всех ссылок на изображения и файлы
  224. #
  225. open (LOGLINKS, ">$fullpath/cleaner_select.txt"); print (LOGLINKS $date);
  226.  
  227. foreach my $tabfil (@result) {
  228. $request = "SELECT @$tabfil[1] FROM @$tabfil[0];";
  229. # print "$request \n";
  230. print (LOGLINKS "$request \n");
  231. $answer = $db->prepare($request);
  232. $answer->execute;
  233. while (my @row = $answer->fetchrow_array) {
  234. # print "@row \n";
  235. foreach my $src (@row) {
  236. if ($src){
  237. my @src = split (/ |\r|\n/,$src);
  238. foreach $src (@src) {
  239. if ($src =~ m/src=\"\/upload\/(.*)\"/i){
  240. $result{$1} = undef;
  241. # print "$1 \n";
  242. } else {
  243. if ($src =~ m/(.*\/.*\.($extension)$)/i){
  244. $result{$1} = undef;
  245. # print "$1 \n";
  246. }
  247. }
  248. }
  249. }
  250. }
  251. }
  252. $answer->finish;
  253. }
  254.  
  255. $db->disconnect;
  256.  
  257. ###############################################################
  258. # Рекурсивный проход по upload, сравнение с хешем ссылок, удаление(в т.ч. пустых папок)
  259. #
  260. open (LOGDIR, ">$fullpath/cleaner_dirs.txt"); print (LOGDIR $date);
  261. open (LOGFILE, ">$fullpath/cleaner_files.txt"); print (LOGFILE $date);
  262.  
  263. my $ifiles = 0;
  264. my $idirs = 0;
  265.  
  266. foreach my $folder (@folders) {
  267.  
  268. unless (opendir(DIR, $folder)){ print (ERR "Cant open ./upload/$folder/\n"); $prr=''; next;}
  269. closedir (DIR);
  270.  
  271. finddepth \&recur, $folder;
  272.  
  273. sub recur {
  274. if (-d) {
  275. # print " dir $fullpath/$File::Find::name\n";
  276. opendir(DIR, "$fullpath/$File::Find::name") or $prr='', print (ERR "Cant open $File::Find::name: $!\n");
  277. my $dir = (grep !/^\.\.?$/, readdir DIR);
  278. closedir (DIR);
  279. unless ($dir || (grep /^$File::Find::name$/, @folders)){
  280. if ($disabletest){
  281. rmdir ("$fullpath/$File::Find::name") or $prr='', print (ERR "cannot rmdir $fullpath/$File::Find::name: $!\n");
  282. }
  283. $idirs++;
  284. # print "$File::Find::name \n";
  285. print (LOGDIR "$fullpath/$File::Find::name\n");
  286. }
  287. } else {
  288. my $fil = $_;
  289. if ($fil =~ m/.*\.($extension)$/i){
  290. # print "file $fil\n";
  291. unless (exists $result{"$File::Find::name"}){
  292. # print " rm -> $File::Find::name\n";
  293. if ($disabletest){
  294. if ($delete =~ /^true$/){
#2 20 сентября 2019 в 11:47
Вещь безусловно нужная.
Насколько понимаю в отличии от чистильщика от Ris, должна быть меньше нагрузка на сервер.

очиститель папки upload замутил по хрону

@fazer

Проблема в том, что некоторые компоненты загружают в своих настройках технические изображения в те же папи, что и изображения в постах.

Например компонент изображений. Ватермарк в нем грузится тоже в папку upload/000/
Логотипы в шаблонах, логотип в компоненте RSS, и т.д.

И все это сносится при чистке чистильщиком((
Так что чистка по крону далеко не всегда возможна.

Но в целом, если действительно снизит нагрузку…
++

ЗЫ:
Думал о том как сделать, что бы не сносило нужные изображения....

Нужно поле (в компоненте Ris) или у Вас в скрипт как то прописать, поле исключений в которое можно было бы вписывать пути изображений которые исключались из очистки.

Их обычно не очень много, смотреть по инструменту разработчиков где находится и прописывать путь в такое поле...
Тогда можно бы было добиться очистки по крону…
#3 20 сентября 2019 в 12:00


Например компонент изображений. Ватермарк в нем грузится тоже в папку upload/000/
Логотипы в шаблонах, логотип в компоненте RSS, и т.д.

Rainbow
А что нам мешает внести в поиск по базе те поля где описаны эти компоненты ?
т.е. скажите какие таблицы/поля проверять на наличие этих картинок
#4 20 сентября 2019 в 12:02

А что нам мешает внести в поиск по базе те поля где описаны эти компоненты ?

@fazer

Дописал пост выше, как наверное это могло бы быть...

скажите какие таблицы/поля проверять на наличие этих картинок

@fazer

Так нет их этих полей в БД (насколько знаю), может ошибаюсь и не нашел...

ЗЫ:
Хм… точно… поля должны быть, просто надо найти…
#5 20 сентября 2019 в 12:07


поле исключений в которое можно было бы вписывать пути изображений которые исключались из очистки.

Rainbow
это одна папка или список папок?
это дерево или можно тупо по имени/именам фильтровать?

А мошть нам разработчики подскажут есть ли такие поля в базе или как можно отфильтровать по именам не проверяемые папки?
#6 20 сентября 2019 в 12:24
Нашел))

Например для компонента изображений таблица _images_presets, а поле для ватермарков wm_image (можно смело добавить в скрипт, "где искать")...

Так что мой вопрос снимается)...
Надо мне лучше было искать..).
#7 20 сентября 2019 в 12:27


Так что мой вопрос снимается)...

Rainbow
подождите, в каком формате они хранятся ?,
мой скрипт ищет по тегу <img src=
оно там так?
#8 20 сентября 2019 в 12:42

ищет по тегу <img src=

@fazer

Нет. Там без тега так :

  1.  
  2. original: 000/u1/1/b/69c0e671.png
  3. small: 000/u1/9/5/bolshoi-wm-image-small.png
  4.  
Так что получается поле о котором раньше писал, все таки нужно...

Даже если всё и найдется в БД (а я например так и не нашел поле от компонента PWA там в админке две иконки грузится), на мой взгляд проще админу записать не проверяемые скриптом пути исключения (от удаления), чем лазить по таблицам...

ЗЫ: вобщем проблема такая есть, а уж как решить её…
Я предложил, но не знаю насколько реализуемо.
#9 20 сентября 2019 в 12:50
по таблицам лазить не трудно, одной больше одной меньше....
а вот папки фильтровать боюсь не получится если они(папки) формируются так же динамически как и все остальные


original: 000/u1/1/b/69c0e671.png
small: 000/u1/9/5/bolshoi-wm-image-small.png

Rainbow
думаю вы ещё не полностью выложились, поисчите плз есчё,
должны быть теги иначе как они рисунки кажут та :)))

если у вас есть phpmyadmin попробуйте в нем поискать по имени рисунка "69c0e671.png"
#10 20 сентября 2019 в 13:05

должны быть теги иначе как они рисунки кажут та :

@fazer

В компонете написаны теги, а в них выводятся только пути из БД...
Я не программист...

Но тегов нет:

#11 20 сентября 2019 в 13:14
Хочу поддержать тему, хорошо бы данный функционал в систему внедрить
#12 20 сентября 2019 в 13:18

yum install libdbi-perl libdbd-mysql-perl
ну или как у вас там на серваке

@fazer
У меня на серваке так:
No package libdbi-perl available.
No package libdbd-mysql-perl available.
Error: Nothing to do
#13 20 сентября 2019 в 13:18
@fazer, в принципе если от скрипта будет нагрузка меньше на сервер, чем от компонента, он будет востребован, кто то не может себе позволить компонент по этой причине...

Я пока занят. Может кто еще что напишет…
#14 20 сентября 2019 в 13:27


No package libdbi-perl available.
No package libdbd-mysql-perl available.

Ris
У вас видимо как то так
yum install perl-DBI
yum install perl-DBD-MySQL
#15 20 сентября 2019 в 13:32


кто то не может себе позволить компонент по этой причине...

Rainbow
согласен, если он(скрипт) будет терять картинки это — гомно :)
я думаю можно придумать признак по которому искать эти картинки,
просто у меня таковых нет и надо как то их смоделировать

зы
думаю стоит просто тупо по расширению (jpg, gif, png) попробовать, должно прокатить :)
Вы не можете отвечать в этой теме.
Войдите или зарегистрируйтесь, чтобы писать на форуме.
Используя этот сайт, вы соглашаетесь с тем, что мы используем файлы cookie.