Created
October 18, 2012 09:28
-
-
Save alfaproject/3910694 to your computer and use it in GitHub Desktop.
PHP script to optimize images file size recursively in a given in a folder
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
* Strip JFIF headers from the JPEG image on stdin, and write | |
* the result to stdout. Won't work unmodified on Windows | |
* because of the text/binary problem. Not thoroughly tested. | |
*/ | |
#include <stdio.h> | |
#include <stdlib.h> | |
void fail(const char* msg) { | |
fputs(msg, stderr); | |
exit(1); | |
} | |
void copy_rest(FILE* f, FILE* g) { | |
char buf[4096]; | |
int len; | |
while ((len = fread(buf, 1, sizeof buf, f)) > 0) { | |
if (fwrite(buf, 1, len, g) != len) | |
fail("write error"); | |
} | |
if (len < 0) | |
fail("read error"); | |
} | |
void skip_jfif(FILE* f, FILE* g) { | |
int a,b; | |
a = getc(f); b = getc(f); | |
if (a != 0xFF || b != 0xD8) | |
fail("not a JPEG file"); | |
putc(a,g); putc(b,g); | |
// 0xFFE9 is APP0 marker to begin JFIF segment | |
while (a = getc(f), b = getc(f), a == 0xFF && b == 0xE0) { | |
// Next 2 bytes after APP0 are length of JFIF segment *including* APP0 | |
// so seek forward (0x???? - 2) bytes | |
a = getc(f); b = getc(f); | |
if (a == 0xEF || b == 0xEF) | |
fail("stop confusing me with weird test cases"); | |
fseek(f, a * 256 + b - 2, SEEK_CUR); | |
} | |
if (a != 0xEF) putc(a,g); | |
if (b != 0xEF) putc(b,g); | |
copy_rest(f,g); | |
} | |
int main() { | |
skip_jfif(stdin,stdout); | |
return 0; | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/local/bin/php | |
<?php | |
function do_gif($image) | |
{ | |
$iniTime = microtime(true); | |
$iniSize = filesize($image); | |
// install gifsicle http://www.lcdf.org/gifsicle/ | |
system('gifsicle -b --careful -k 256 -O3 '. escapeshellarg($image)); | |
clearstatcache(true, $image); | |
$endSize = filesize($image); | |
$endTime = round((microtime(true) - $iniTime) * 1000); | |
$sizeGain = round($endSize / $iniSize * 100, 1); | |
if ($endSize != $iniSize) { | |
echo "Time: {$endTime}ms\tSize: $sizeGain% ($iniSize > $endSize)\t$image\n"; | |
return ($iniSize - $endSize); | |
} else { | |
echo "Time: {$endTime}ms\tSize: $iniSize\t$image\n"; | |
return 0; | |
} | |
} | |
function do_jpeg($image) | |
{ | |
$iniTime = microtime(true); | |
$iniSize = filesize($image); | |
// create 2 temporary files | |
$tmp1 = tempnam(dirname($image), 'imgopt_'); | |
$tmp2 = tempnam(dirname($image), 'imgopt_'); | |
// jpegtran is part of libjpeg (almost surely already on your system, if not, it's here: http://www.ijg.org/) | |
system('jpegtran -copy none -optimize -outfile '. escapeshellarg($tmp1) .' '. escapeshellarg($image)); | |
// jfifremove is included with this script, be sure to compile and install | |
system('jfifremove < '. escapeshellarg($tmp1) .' > '. escapeshellarg($tmp2)); | |
unlink($tmp1); | |
$endSize = filesize($tmp2); | |
$endTime = round((microtime(true) - $iniTime) * 1000); | |
$sizeGain = round($endSize / $iniSize * 100, 1); | |
if ($endSize > 0 && $endSize < $iniSize) { | |
rename($tmp2, $image); | |
echo "Time: {$endTime}ms\tSize: $sizeGain% ($iniSize > $endSize)\t$image\n"; | |
return ($iniSize - $endSize); | |
} else { | |
unlink($tmp2); | |
echo "Time: {$endTime}ms\tSize: $iniSize\t$image\n"; | |
return 0; | |
} | |
} | |
function do_png($image) | |
{ | |
$iniTime = microtime(true); | |
$iniSize = filesize($image); | |
// create a temporary file | |
$tmp = tempnam(dirname($image), 'imgopt_'); | |
// OptiPNG | |
// http://optipng.sourceforge.net/ | |
unlink($tmp); | |
system('optipng -q -zc1-9 -zm1-9 -zs0-3 -f0-5 -out '. escapeshellarg($tmp) .' '. escapeshellarg($image)); | |
// pngout | |
// http://www.jonof.id.au/kenutils | |
system('pngout -q -y '. escapeshellarg($tmp)); | |
if (file_exists($tmp .'.png')) { | |
rename($tmp .'.png', $tmp); | |
} elseif (file_exists($tmp .'.PNG')) { | |
rename($tmp .'.PNG', $tmp); | |
} | |
// re-compress using 7zip algorithm | |
// advpng (part of AdvanceCOMP) | |
// http://advancemame.sourceforge.net/comp-readme.html | |
system('advpng -q -z4 '. escapeshellarg($tmp)); | |
$endSize = filesize($tmp); | |
$endTime = round((microtime(true) - $iniTime) * 1000); | |
$sizeGain = round($endSize / $iniSize * 100, 1); | |
if ($endSize > 0 && $endSize < $iniSize) { | |
rename($tmp, $image); | |
echo "Time: {$endTime}ms\tSize: $sizeGain% ($iniSize > $endSize)\t$image\n"; | |
return ($iniSize - $endSize); | |
} else { | |
unlink($tmp); | |
echo "Time: {$endTime}ms\tSize: $iniSize\t$image\n"; | |
return 0; | |
} | |
} | |
function recurseImages($dir, &$initialBytes, &$bytesSaved) | |
{ | |
$images = $initialBytes = $bytesSaved = 0; | |
$stack[] = $dir; | |
while ($stack) { | |
sort($stack); | |
$thisdir = array_pop($stack); | |
if ($dircont = scandir($thisdir)) { | |
for ($i = 0; isset($dircont[$i]); $i++) { | |
if ($dircont[$i]{0} !== '.') { | |
$current_file = $thisdir .'/'. $dircont[$i]; | |
if (!is_link($current_file)) { | |
if (is_dir($current_file)) { | |
$stack[] = $current_file; | |
} else { | |
switch (strtoupper(pathinfo($current_file, PATHINFO_EXTENSION))) { | |
case 'GIF': | |
$initialBytes += filesize($current_file); | |
$bytesSaved += do_gif($current_file); | |
chmod($current_file, 0644); | |
$images++; | |
break; | |
case 'PNG': | |
$initialBytes += filesize($current_file); | |
$bytesSaved += do_png($current_file); | |
chmod($current_file, 0644); | |
$images++; | |
break; | |
case 'JPG': | |
case 'JPEG': | |
$initialBytes += filesize($current_file); | |
$bytesSaved += do_jpeg($current_file); | |
chmod($current_file, 0644); | |
$images++; | |
break; | |
} | |
} | |
} | |
} | |
} | |
} | |
} | |
return $images; | |
} | |
if (is_dir($argv[1])) { | |
$time = time(); | |
$initialBytes = $bytesSaved = 0; | |
$path = realpath($argv[1]); | |
$images = recurseImages($path, $initialBytes, $bytesSaved); | |
$time = time() - $time; | |
echo "Time: {$time}s\tImages: $images\tInitial bytes: $initialBytes\tBytes saved: $bytesSaved (". round($bytesSaved / $initialBytes * 100, 1) ."%)\n"; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment