This one was little bit tricky. If you want to pack files on your IIS webserver follow my guide.
At first please install 7-zip on your server. It should be in a following path by default:
C:\Program Files (x86)\7-Zip
Or without (x86) if you installed 64 bit version…
PHP part
Command in your script:
$packingCommand = 'c:"\\Program Files (x86)\\7-Zip\\7z.exe" a -tzip -mx0 -mmt4 -wc:\Windows\Temp -r %(name)s %(path)s\*';
Let’s break it down:
- a – means add to archive
- tzip – means we’re selecting zip method (t stands for type)
- mx0 – I’m using zip compression with level set to 0 because I only want to create a zip container (it work’s really fast this way – about 1s for 512MB file on my machine).
- mmt4 – means that 7zip should use 4 CPU threads.
- wc – sets working dir (you should use something writable by server process).
- r – means recursive directory traversing.
If you want to tweak it, you can find full documentation here and here. Please remember to add m if you’re passing something to the specific method!
What’s next?
I use custom function to replace handlers in a command string. It’s called vsprintf_named and looks like this:
/**
* vsprintf with ability to take named params.
* Usage: vpsrintf_named( "$(name)s is %(age)02d", [ 'name' => 'John', 'age' => 30 ] );
*
* @param string $format
* @param array $args
*/
function vsprintf_named($format, $args) {
$names = preg_match_all('/%\((.*?)\)/', $format, $matches, PREG_SET_ORDER);
$values = array();
foreach($matches as $match) {
$values[] = $args[$match[1]];
}
$format = preg_replace('/%\((.*?)\)/', '%', $format);
return vsprintf($format, $values);
}
Now, let’s generate full command for Windows:
/** @var $command **/
$command = vsprintf_named( $packingCommand, [
'name' => 'c:\your_file.zip',
'path' => 'c:\dir\to\pack',
] );
And finally:
exec( $command, $output, $return );
And voilà! You can debug this using $output and $return vars.
Simply $return should be set to 0 if everything went good. You can view function output simply like that:
echo implode( ', ', $output );
Bonus:
Here is full benchmark code that I use to check if everything works:
File: benchmark.php
-------------------
<?php
/**
* Benchmark
*
* @category debug
*/
public function benchmark() {
/** @var integer $timeStart **/
$timeStart = microtime(true);
echo 'RUNNING BENCHMARK, PLEASE WAIT.</br>'. PHP_EOL;
/** @var string $name **/
$name = 'test.zip';
set_time_limit( 600 );
/** @var string $packageDir **/
$packageDir = Config::$tmpDir . DIRECTORY_SEPARATOR . 'test';
if( file_exists( $packageDir ) ) {
/**
* Select packing method
*/
// internal zipArchive
if( Config::$packingMethod == 'internal' ) {
$zipArchive = new ZipArchive();
$zipArchive->open( Config::$uploadPath . DIRECTORY_SEPARATOR . $name, \ZipArchive::CREATE);
$zipArchive->addDirectory( Config::$tmpDir . DIRECTORY_SEPARATOR . 'test' );
$zipArchive->close();
}
// command line
elseif( Config::$packingMethod == 'command' ) {
echo 'USING COMMAND LINE MODE!<br />' . PHP_EOL;
/** @var $command **/
$command = vsprintf_named( Config::$packingCommand, [
'name' => Config::$uploadPath . DIRECTORY_SEPARATOR . $name,
'path' => Config::$tmpDir . DIRECTORY_SEPARATOR . 'test',
] );
exec( $command, $output, $return );
echo $command . '<br />' . PHP_EOL;
echo 'RUNNING AS: ' . get_current_user() . '<br />' . PHP_EOL;
echo implode( ', ', $output ) . ' WITH CODE ' . $return . '<br />' . PHP_EOL;
// Return will return non-zero upon an error
if( $return !== 0) {
$error = true;
echo 'COMMAND LINE ERROR!<br />' . PHP_EOL;
}
}
} else {
echo "SORRY BUT TEST DIR DOES NOT EXIST. PLEASE CREATE IT.</br>" . PHP_EOL;
}
/** @var float $time Execution time. **/
$time = number_format(( microtime(true) - $timeStart), 4);
echo "FINISHED SUCCESSFULLY IN: $time SECONDS.</br>" . PHP_EOL;
}
Comments
0 responses to “How to pack directory using 7-zip in PHP on Windows?”
On my local PC,exec() always return 1, even the command is: 7z.exe –help. What happend ?