Php прочитать файл из архива
Рассказ будет о том, как работать с маленьким PHP классом PclZip, в котором всего 5698 строк с комментариями. Как уже можно было догадаться из названия этот класс для работы с Zip архивами. Я в сжатой форме познакомлю вас с данным классом.
1. Создание архива с нуля
Для создания архива с нуля пишем:
include_once(‘pclzip.lib.php’); //Подключаем библиотеку.
$archive = new PclZip('archive.zip'); //Создаём объект и в качестве аргумента, указываем название архива, с которым работаем.
$result = $archive->create('file.txt'); // Этим методом класса мы создаём архив с заданным выше названием
var_dump($result); // Если всё прошло хорошо, возращаем массив с данными (время создание архива, занесённым файлом и т.д)
if($result == 0) echo $archive->errorInfo(true); //Возращает причину ошибки
>
?>
Но если вам надо заархивировать например файл file.txt, который лежит в папке folder и надо сделать так, чтобы file.txt лежал в корне архива, а не полность папка folder с file.txt, можно сделать так
$result = $archive->create('folder/file.txt', PCLZIP_OPT_REMOVE_PATH, 'folder');
?>
Если надо взять файл из одной папки и переместить его в архиве в другую папку, пишем
$result = $archive->create('folder/file.txt', PCLZIP_OPT_REMOVE_PATH, 'folder', PCLZIP_OPT_ADD_PATH, 'install');
?>
То есть мы взяли файл file.txt в папке folder, вытащили и поместили в архиве в папку install.
Для добавления комметария к архиву надо добавить к create PCLZIP_OPT_COMMENT, ‘Комментарий’.
2. Добавление файлов в архив.
Здесь практически всё тоже самое, что и при создании архива с 0.
include_once(‘pclzip.lib.php’);
$archive = new PclZip('archive.zip'); //Указываем существующий архив
if($archive->add('file.txt') == 0) echo $archive->errorInfo(true);
?>
Метод add тоже может использовать PCLZIP_OPT_REMOVE_PATH и PCLZIP_OPT_ADD_PATH, что логично.
3. Вывод содержимого архива
Для этой операции у нас в наличии есть метод listContent(). По сути этот метод возращает ту же информацию, что и предыдущие два метода, после заверщения их работы.
$archive = new PclZip('archive.zip');
$result = $archive->listContent();
if ($result == 0) < //Возращает 0, если операция не удалась.
echo $archive->errorInfo(true); //Вывод ошибки.
> else foreach($result as $id=> $v1) < // перебор массива с файлами. В переменную $id попадает индекс файла в архиве, который пригодится нам далее, а переменная $v1 становится массивом с данными о файле.
foreach($v1 as $kk=>$v2) < // перебор массива с информацией о файле в архиве
echo "$kk - $v2 ";
>
>
>
?>
4. Удаление файлов из архива
Для удаление всех файлов из архива прибегним к методу
$archive = new PclZip('archive.zip');
$result = $archive->delete();
if($result == 0) echo $archive->errorInfo(true);
?>
После этого архив останется просто пустой оболочкой. Для удаления одиночных файлов, нам надо сделать так:
$archive->delete(PCLZIP_OPT_BY_INDEX, '1,2');
?>
Этим самым мы указываем, какие файлы удалять. Мы указываем через запятую индификатор файла, которого мы хотим удалить. Индификаторы мы можем получить через функцию, которая рассматривалась выше (listContent).
5. Разархивирование
$archive = new PclZip('archive.zip');
$result = $archive->extract();
if($result == 0) echo $archive->errorInfo(true);
?>
В этом случае будет разархивирован весь архив. Для разархивации в папку делаем так.
$result = $archive->extract(PCLZIP_OPT_PATH, 'folder');
?>
Вот и всё. Это была обзорная статья и она раскрыла не все возможности данного класса, но то что описано здесь, достаточно для обычного использования. Ах да, если заменить zip на rar, то тоже будет всё нормально работать.
Открывает новый или существующий ZIP-архив для чтения, записи или изменения.
Начиная с libzip 1.6.0, пустой файл больше не является корректным архивом.
Список параметров
Имя ZIP-архива для открытия.
Возвращаемые значения
Возвращает true в случае успешного выполнения или один из следующих кодов ошибки: ZipArchive::ER_EXISTS Файл уже существует. ZipArchive::ER_INCONS Несовместимый ZIP-архив. ZipArchive::ER_INVAL Недопустимый аргумент. ZipArchive::ER_MEMORY Ошибка динамического выделения памяти. ZipArchive::ER_NOENT Нет такого файла. ZipArchive::ER_NOZIP Не является ZIP-архивом. ZipArchive::ER_OPEN Невозможно открыть файл. ZipArchive::ER_READ Ошибка чтения. ZipArchive::ER_SEEK Ошибка поиска.
User Contributed Notes 21 notes
With php 5.2.6, the following code created a new zip or replaced a existing zip.
Note that I am only using the ZIPARCHIVE::OVERWRITE flag.
$zip = new ZipArchive ();
$opened = $zip -> open ( $zipFileName , ZIPARCHIVE :: OVERWRITE );
if( $opened !== true ) die( "cannot open < $zipFileName >for writing." );
>
$zip -> addFromString ( $name , $contents );
$zip -> close ();
?>
Now, with php 5.2.8, it does not work and gives this warning:
Warning: ZipArchive::addFromString() [ziparchive.addfromstring]: Invalid or unitialized Zip object in [myfile] on line [myline]
To fix this, you must specify the flags as create or overwrite.
$zip = new ZipArchive ();
$opened = $zip -> open ( $zipFileName , ZIPARCHIVE :: CREATE | ZIPARCHIVE :: OVERWRITE );
if( $opened !== true ) die( "cannot open < $zipFileName >for writing." );
>
$zip -> addFromString ( $name , $contents );
$zip -> close ();
?>
When googling for the error message I found a lot of people that had it but couldn't figure out why they were getting it.
I hope this helps someone.
class zip extends ZipArchive
public function message ( $code )
switch ( $code )
case 0 :
return 'No error' ;
case 1 :
return 'Multi-disk zip archives not supported' ;
case 2 :
return 'Renaming temporary file failed' ;
case 3 :
return 'Closing zip archive failed' ;
case 4 :
return 'Seek error' ;
case 5 :
return 'Read error' ;
case 6 :
return 'Write error' ;
case 7 :
return 'CRC error' ;
case 8 :
return 'Containing zip archive was closed' ;
case 9 :
return 'No such file' ;
case 10 :
return 'File already exists' ;
case 11 :
return 'Can\'t open file' ;
case 12 :
return 'Failure to create temporary file' ;
case 13 :
return 'Zlib error' ;
case 14 :
return 'Malloc failure' ;
case 15 :
return 'Entry has been changed' ;
case 16 :
return 'Compression method not supported' ;
case 17 :
return 'Premature EOF' ;
case 18 :
return 'Invalid argument' ;
case 19 :
return 'Not a zip archive' ;
case 20 :
return 'Internal error' ;
case 21 :
return 'Zip archive inconsistent' ;
case 22 :
return 'Can\'t remove file' ;
case 23 :
return 'Entry has been deleted' ;
default:
return 'An unknown error has occurred(' . intval ( $code ). ')' ;
>
>
public function isDir ( $path )
return substr ( $path ,- 1 ) == '/' ;
>
public function getTree ()
$Tree = array();
$pathArray = array();
for( $i = 0 ; $i < $this ->numFiles ; $i ++)
$path = $this -> getNameIndex ( $i );
$pathBySlash = array_values ( explode ( '/' , $path ));
$c = count ( $pathBySlash );
$temp = & $Tree ;
for( $j = 0 ; $j < $c - 1 ; $j ++)
if(isset( $temp [ $pathBySlash [ $j ]]))
$temp = & $temp [ $pathBySlash [ $j ]];
else
$temp [ $pathBySlash [ $j ]] = array();
$temp = & $temp [ $pathBySlash [ $j ]];
>
if( $this -> isDir ( $path ))
$temp [ $pathBySlash [ $c - 1 ]] = array();
else
$temp [] = $pathBySlash [ $c - 1 ];
>
return $Tree ;
>
>
?>
// Use ZipArchive::OVERWRITE when the targetd file does not exist may lead you to an error like this
// Warning: ZipArchive::addFile(): Invalid or uninitialized Zip object
// try ZipArchive::OVERWRITE|ZipArchive::CREATE when you want to replace a zip archive that may not exist
$zip = new ZipArchive ;
$rt = $zip -> open ( 'i.zip' , ZipArchive :: OVERWRITE );
echo $rt ;
// when i.zip does not exist, $rt is 9, ZipArchive::ER_NOENT, or "No such file."
$zip -> addFile ( 'wuxiancheng.cn.sql' , 'db.sql' );
// triggers an error with the message "Warning: ZipArchive::addFile(): Invalid or uninitialized Zip object . "
// Use ZipArchive::OVERWRITE|ZipArchive::CREATE
$zip = new ZipArchive ;
$zip -> open ( 'i.zip' , ZipArchive :: OVERWRITE | ZipArchive :: CREATE );
$zip -> addFile ( 'wuxiancheng.cn.sql' , 'db.sql' );
?>
Calling ZipArchive->open() will not create an empty zip archive file.
I found this out the hard way. I wrote code that produced positive
results: I.E. the return value from the call to ZipArchive was TRUE
and the empty zip file was not created. So at least call
ZipArchive->addFromString(, '')
when creating a new zip archive file.
If you have archives that you want to overwrite just use:
It will overwrite existing archives and at the same time create new ones if they don't already exist.
ZIPARCHIVE::OVERWRITE won't work for both of these scenarios.
(PHP version 5.4.4)
if you are echoing out the output and confused about the number. maybe this will help. i'm not totally sure it is accurate though.
ZIPARCHIVE::ER_EXISTS - 10
ZIPARCHIVE::ER_INCONS - 21
ZIPARCHIVE::ER_INVAL - 18
ZIPARCHIVE::ER_MEMORY - 14
ZIPARCHIVE::ER_NOENT - 9
ZIPARCHIVE::ER_NOZIP - 19
ZIPARCHIVE::ER_OPEN - 11
ZIPARCHIVE::ER_READ - 5
ZIPARCHIVE::ER_SEEK - 4
Even though the api specifies that the flags are optional I found that I had to specify the flag ZIPARCHIVE::CREATE for an archive to be opened.
This is on a Windows 7 system with PHP 5.3.0
If the directory you are writing or saving into does not have the correct permissions set, you won't get any error messages and it will look like everything worked fine. except it won't have changed!
Instead make sure you collect the return value of ZipArchive::close(). If it is false. it didn't work.
$zip = new ZipArchive ;
$res = $zip -> open ( 'test.zip' , ZipArchive :: CREATE );
if ( $res === TRUE ) //CODE GOES HERE
$zip -> close ();
> else switch( $res ) case ZipArchive :: ER_EXISTS :
$ErrMsg = "File already exists." ;
break;
case ZipArchive :: ER_INCONS :
$ErrMsg = "Zip archive inconsistent." ;
break;
case ZipArchive :: ER_MEMORY :
$ErrMsg = "Malloc failure." ;
break;
case ZipArchive :: ER_NOENT :
$ErrMsg = "No such file." ;
break;
case ZipArchive :: ER_NOZIP :
$ErrMsg = "Not a zip archive." ;
break;
case ZipArchive :: ER_OPEN :
$ErrMsg = "Can't open file." ;
break;
case ZipArchive :: ER_READ :
$ErrMsg = "Read error." ;
break;
case ZipArchive :: ER_SEEK :
$ErrMsg = "Seek error." ;
break;
default:
$ErrMsg = "Unknow (Code $rOpen )" ;
break;
ZipArchive::OVERWRITE does NOT mean an existing file would be deleted when ZipArchive::open() is called.
In fact, the existing file will be deleted before PHP saves the zip archive on disk.
PHP takes these steps to finish zipping:
1. When ZipArchive::open('xx.zip') is called
If 'xx.zip' exists and is a zip archive, it will be opened and read as a temporary zip file,
If the file does not exist, and ZipArchive::CREATE is applied, php will create a temporary empty zip file
In these cases, ZipArchive::open() returns true, otherwise it returns an integer error code.
2. Adds file(s) to the temporary zip file when methods such as addFile(), addFromString() are called.
3. Deletes the existing file before saving the temporary zip file on disk.
4. Save the temporary zip file on disk
5. Closes the active archive when ZipArchive::close() is called or at the end of the script
Since PHP does NOT delete the existing file before saving the zip archive on disk, you should use unset() to delete it if you want to zip that file's containing folder and save the zip archive in that folder, otherwise you will get a larger and larger zip archive everytime you refresh the page.
return values of ZipArchive::open() and their values and meanings
ZipArchive::ER_SEEK 4 Seek error.
ZipArchive::ER_READ 5 Read error.
ZipArchive::ER_NOENT 9 No such file.
ZipArchive::ER_OPEN 11 Can't open file.
ZipArchive::ER_EXISTS 10 File already exists.
ZipArchive::ER_MEMORY 14 Malloc failure.
ZipArchive::ER_INVAL 18 Invalid argument.
ZipArchive::ER_NOZIP 19 Not a zip archive.
ZipArchive::ER_INCONS 21 Zip archive inconsistent
If on PHP 8.0+, you can use match expression to decode status code:
$archive = new \ ZipArchive ();
$result = $archive -> open ( 'some.file.zip' );
$message = match ( $result ) \ ZipArchive :: ER_MULTIDISK => 'Multi-disk zip archives not supported' ,
\ ZipArchive :: ER_RENAME => 'Renaming temporary file failed' ,
\ ZipArchive :: ER_CLOSE => 'Closing zip archive failed' ,
\ ZipArchive :: ER_SEEK => 'Seek error' ,
\ ZipArchive :: ER_READ => 'Read error' ,
\ ZipArchive :: ER_WRITE => 'Write error' ,
\ ZipArchive :: ER_CRC => 'CRC error' ,
\ ZipArchive :: ER_ZIPCLOSED => 'Containing zip archive was closed' ,
\ ZipArchive :: ER_NOENT => 'No such file' ,
\ ZipArchive :: ER_EXISTS => 'File already exists' ,
\ ZipArchive :: ER_OPEN => 'Can\'t open file' ,
\ ZipArchive :: ER_TMPOPEN => 'Failure to create temporary file' ,
\ ZipArchive :: ER_ZLIB => 'Zlib error' ,
\ ZipArchive :: ER_MEMORY => 'Malloc failure' ,
\ ZipArchive :: ER_CHANGED => 'Entry has been changed' ,
\ ZipArchive :: ER_COMPNOTSUPP => 'Compression method not supported' ,
\ ZipArchive :: ER_EOF => 'Premature EOF' ,
\ ZipArchive :: ER_INVAL => 'Invalid argument' ,
\ ZipArchive :: ER_NOZIP => 'Not a zip archive' ,
\ ZipArchive :: ER_INTERNAL => 'Internal error' ,
\ ZipArchive :: ER_INCONS => 'Zip archive inconsistent' ,
\ ZipArchive :: ER_REMOVE => 'Can\'t remove file' ,
\ ZipArchive :: ER_DELETED => 'Entry has been deleted' ,
// \ZipArchive::ER_OK => 'No error',
default => 'No error' ,
>;
?>
Извлечение всего архива или его части в указанное место назначения.
Разрешения по умолчанию для извлечённых файлов и каталогов предоставляют максимально широкий доступ. Это можно ограничить установкой текущего umask, который изменяется с помощью umask() .
Примеры
$zip = new ZipArchive ;
if ( $zip -> open ( 'test.zip' ) === TRUE ) $zip -> extractTo ( '/my/destination/dir/' );
$zip -> close ();
echo 'готово' ;
> else echo 'ошибка' ;
>
?>?php
$zip = new ZipArchive ;
$res = $zip -> open ( 'test_im.zip' );
if ( $res === TRUE ) $zip -> extractTo ( '/my/destination/dir/' , array( 'pear_item.jpg' , 'testfromfile.php' ));
$zip -> close ();
echo 'готово' ;
> else echo 'ошибка' ;
>
?>?php
User Contributed Notes 16 notes
If you want to copy one file at a time and remove the folder name that is stored in the ZIP file, so you don't have to create directories from the ZIP itself, then use this snippet (basically collapses the ZIP file into one Folder).
?>
* On a side note, you can also use $_FILES['userfile']['tmp_name'] as the $path for an uploaded ZIP so you never have to move it or extract a uploaded zip file.
ProNeticas Dev Team
If you want to extract the files just to the current folder, simply use
$zip->extractTo(".");
It took me hours to figure this out.
The extractTo() method does not offer any parameter to allow extracting files and folders recursively from another (parent) folder inside the ZIP archive. With the following method it is possible:
class my_ZipArchive extends ZipArchive
public function extractSubdirTo ( $destination , $subdir )
$errors = array();
// Prepare dirs
$destination = str_replace (array( "/" , "\\" ), DIRECTORY_SEPARATOR , $destination );
$subdir = str_replace (array( "/" , "\\" ), "/" , $subdir );
if ( substr ( $destination , mb_strlen ( DIRECTORY_SEPARATOR , "UTF-8" ) * - 1 ) != DIRECTORY_SEPARATOR )
$destination .= DIRECTORY_SEPARATOR ;
if ( substr ( $subdir , - 1 ) != "/" )
$subdir .= "/" ;
// Extract files
for ( $i = 0 ; $i < $this ->numFiles ; $i ++)
$filename = $this -> getNameIndex ( $i );
if ( substr ( $filename , 0 , mb_strlen ( $subdir , "UTF-8" )) == $subdir )
$relativePath = substr ( $filename , mb_strlen ( $subdir , "UTF-8" ));
$relativePath = str_replace (array( "/" , "\\" ), DIRECTORY_SEPARATOR , $relativePath );
if ( mb_strlen ( $relativePath , "UTF-8" ) > 0 )
if ( substr ( $filename , - 1 ) == "/" ) // Directory
// New dir
if (! is_dir ( $destination . $relativePath ))
if (!@ mkdir ( $destination . $relativePath , 0755 , true ))
$errors [ $i ] = $filename ;
>
else
if ( dirname ( $relativePath ) != "." )
if (! is_dir ( $destination . dirname ( $relativePath )))
// New dir (for file)
@ mkdir ( $destination . dirname ( $relativePath ), 0755 , true );
>
>
// New file
if (@ file_put_contents ( $destination . $relativePath , $this -> getFromIndex ( $i )) === false )
$errors [ $i ] = $filename ;
>
>
>
>
$zip = new my_ZipArchive ();
if ( $zip -> open ( "test.zip" ) === TRUE )
$errors = $zip -> extractSubdirTo ( "C:/output" , "folder/subfolder/" );
$zip -> close ();
echo 'ok, errors: ' . count ( $errors );
>
else
echo 'failed' ;
>
?>
Примеры сохранения и чтения текстовых данных и массивов в файлы.
User Contributed Notes 15 notes
Zip a folder (include itself).
Usage:
HZip::zipDir('/path/to/sourceDir', '/path/to/out.zip');
class HZip
<
/**
* Add files and sub-directories in a folder to zip file.
* @param string $folder
* @param ZipArchive $zipFile
* @param int $exclusiveLength Number of text to be exclusived from the file path.
*/
private static function folderToZip ( $folder , & $zipFile , $exclusiveLength ) <
$handle = opendir ( $folder );
while ( false !== $f = readdir ( $handle )) <
if ( $f != '.' && $f != '..' ) <
$filePath = " $folder / $f " ;
// Remove prefix from file path before add to zip.
$localPath = substr ( $filePath , $exclusiveLength );
if ( is_file ( $filePath )) <
$zipFile -> addFile ( $filePath , $localPath );
> elseif ( is_dir ( $filePath )) <
// Add sub-directory.
$zipFile -> addEmptyDir ( $localPath );
self :: folderToZip ( $filePath , $zipFile , $exclusiveLength );
>
>
>
closedir ( $handle );
>
/**
* Zip a folder (include itself).
* Usage:
* HZip::zipDir('/path/to/sourceDir', '/path/to/out.zip');
*
* @param string $sourcePath Path of directory to be zip.
* @param string $outZipPath Path of output zip file.
*/
public static function zipDir ( $sourcePath , $outZipPath )
<
$pathInfo = pathInfo ( $sourcePath );
$parentPath = $pathInfo [ 'dirname' ];
$dirName = $pathInfo [ 'basename' ];
$z = new ZipArchive ();
$z -> open ( $outZipPath , ZIPARCHIVE :: CREATE );
$z -> addEmptyDir ( $dirName );
self :: folderToZip ( $sourcePath , $z , strlen ( " $parentPath /" ));
$z -> close ();
>
>
?>
With PHP 5.6+, you may come up with theses errors.
Warning: Unknown: Cannot destroy the zip context in Unknown on line 0
Warning: ZipArchive::close(): Can't remove file: No such file or directory in xxxx.php on line xx
Warning: Unknown: Cannot destroy the zip context in Unknown on line 0
So, don't forget to put at least one file to your zip archive.
A way of zipping files and downloading them thereafter:
$files = array( 'image.jpg' , 'text.txt' , 'music.wav' );
$zipname = 'enter_any_name_for_the_zipped_file.zip' ;
$zip = new ZipArchive ;
$zip -> open ( $zipname , ZipArchive :: CREATE );
foreach ( $files as $file ) $zip -> addFile ( $file );
>
$zip -> close ();
///Then download the zipped file.
header ( 'Content-Type: application/zip' );
header ( 'Content-disposition: attachment; filename=' . $zipname );
header ( 'Content-Length: ' . filesize ( $zipname ));
readfile ( $zipname );
The following code can be used to get a list of all the file names in a zip file.
$za -> open ( 'theZip.zip' );
for( $i = 0 ; $i < $za ->numFiles ; $i ++ ) <
$stat = $za -> statIndex ( $i );
print_r ( basename ( $stat [ 'name' ] ) . PHP_EOL );
>
?>
Simple class xZip to zip big folders into multiple parts and unzip multi zip files at once.
class xZip public function __construct () <>
private function _rglobRead ( $source , & $array = array()) if (! $source || trim ( $source ) == "" ) $source = "." ;
>
foreach ((array) glob ( $source . "/*/" ) as $key => $value ) $this -> _rglobRead ( str_replace ( "//" , "/" , $value ), $array );
>
foreach ((array) glob ( $source . "*.*" ) as $key => $value ) $array [] = str_replace ( "//" , "/" , $value );
>
>
private function _zip ( $array , $part , $destination ) $zip = new ZipArchive ;
@ mkdir ( $destination , 0777 , true );
if ( $zip -> open ( str_replace ( "//" , "/" , " < $destination >/partz < $part >.zip" ), ZipArchive :: CREATE )) foreach ((array) $array as $key => $value ) $zip -> addFile ( $value , str_replace (array( "../" , "./" ), NULL , $value ));
>
$zip -> close ();
>
>
public function zip ( $limit = 500 , $source = NULL , $destination = "./" ) if (! $destination || trim ( $destination ) == "" ) $destination = "./" ;
>
$this -> _rglobRead ( $source , $input );
$maxinput = count ( $input );
$splitinto = (( $maxinput / $limit ) > round ( $maxinput / $limit , 0 )) ? round ( $maxinput / $limit , 0 ) + 1 : round ( $maxinput / $limit , 0 );
for( $i = 0 ; $i < $splitinto ; $i ++) $this -> _zip ( array_slice ( $input , ( $i * $limit ), $limit , true ), $i , $destination );
>
unset( $input );
return;
>
public function unzip ( $source , $destination ) @ mkdir ( $destination , 0777 , true );
foreach ((array) glob ( $source . "/*.zip" ) as $key => $value ) $zip = new ZipArchive ;
if ( $zip -> open ( str_replace ( "//" , "/" , $value )) === true ) $zip -> extractTo ( $destination );
$zip -> close ();
>
>
>
public function __destruct () <>
>
//$zip = new xZip;
//$zip->zip(500, "images/", "images_zip/");
//$zip->unzip("images_zip/", "images/");
?>
There is a usefull function to get the ZipArchive status as a human readable string :
function ZipStatusString ( $status )
switch( (int) $status )
case ZipArchive :: ER_OK : return 'N No error' ;
case ZipArchive :: ER_MULTIDISK : return 'N Multi-disk zip archives not supported' ;
case ZipArchive :: ER_RENAME : return 'S Renaming temporary file failed' ;
case ZipArchive :: ER_CLOSE : return 'S Closing zip archive failed' ;
case ZipArchive :: ER_SEEK : return 'S Seek error' ;
case ZipArchive :: ER_READ : return 'S Read error' ;
case ZipArchive :: ER_WRITE : return 'S Write error' ;
case ZipArchive :: ER_CRC : return 'N CRC error' ;
case ZipArchive :: ER_ZIPCLOSED : return 'N Containing zip archive was closed' ;
case ZipArchive :: ER_NOENT : return 'N No such file' ;
case ZipArchive :: ER_EXISTS : return 'N File already exists' ;
case ZipArchive :: ER_OPEN : return 'S Can\'t open file' ;
case ZipArchive :: ER_TMPOPEN : return 'S Failure to create temporary file' ;
case ZipArchive :: ER_ZLIB : return 'Z Zlib error' ;
case ZipArchive :: ER_MEMORY : return 'N Malloc failure' ;
case ZipArchive :: ER_CHANGED : return 'N Entry has been changed' ;
case ZipArchive :: ER_COMPNOTSUPP : return 'N Compression method not supported' ;
case ZipArchive :: ER_EOF : return 'N Premature EOF' ;
case ZipArchive :: ER_INVAL : return 'N Invalid argument' ;
case ZipArchive :: ER_NOZIP : return 'N Not a zip archive' ;
case ZipArchive :: ER_INTERNAL : return 'N Internal error' ;
case ZipArchive :: ER_INCONS : return 'N Zip archive inconsistent' ;
case ZipArchive :: ER_REMOVE : return 'S Can\'t remove file' ;
case ZipArchive :: ER_DELETED : return 'N Entry has been deleted' ;
default: return sprintf ( 'Unknown status %s' , $status );
>
>
//use bzip2 + ZipArchive to reduce file size of your zip archives.
$zip = new ZipArchive ;
$zip -> open ( 'i.zip' , ZIPARCHIVE :: CREATE | ZIPARCHIVE :: OVERWRITE );
$file = 'wuxiancheng.cn.sql' ;
$bzFilename = $file . '.bz2' ;
$sql = file_get_contents ( $file );
$sql = bzcompress ( $sql , 9 );
$zip -> addFromString ( $bzFilename , $sql );
$zip -> setArchiveComment ( 'zipped on ' . date ( 'Y-M-d' ));
?>?php
There is a limit withing PHP 5.3.3 (which seems to have been addressed in later versions; 5.3.29 seems ok on a different server).
If you try to open a zip file with more than 65,535 files in it (in my case it had 237,942 files) then you cannot access the later files. The numFiles property only reports the first 65k files.
Read a file from an archive to a variable.
A warning is printed automatically in case of a CRC32 mismatch, which we capture, so we can print our own error message.
$zip = new ZipArchive ();
if ( $zip -> open ( 'archive.zip' )) $fp = $zip -> getStream ( 'myfile.txt' ); //file inside archive
if(! $fp )
die( "Error: can't get stream to zipped file" );
$stat = $zip -> statName ( 'myfile.txt' );
$buf = "" ; //file buffer
ob_start (); //to capture CRC error message
while (! feof ( $fp )) $buf .= fread ( $fp , 2048 ); //reading more than 2156 bytes seems to disable internal CRC32 verification (bug?)
>
$s = ob_get_contents ();
ob_end_clean ();
if( stripos ( $s , "CRC error" ) != FALSE ) echo 'CRC32 mismatch, current ' ;
printf ( "%08X" , crc32 ( $buf )); //current CRC
echo ', expected ' ;
printf ( "%08X" , $stat [ 'crc' ]); //expected CRC
>
fclose ( $fp );
$zip -> close ();
//Done, unpacked file is stored in $buf
>
?>
To create a corrupt file, change a byte in a zip file using a hex editor.
Hi there.
I just wrote a little function to zip a whole folder while maintaining the dir-structure. I hope it might help someone.
function folderToZip ( $folder , & $zipFile , $subfolder = null ) if ( $zipFile == null ) // no resource given, exit
return false ;
>
// we check if $folder has a slash at its end, if not, we append one
$folder .= end ( str_split ( $folder )) == "/" ? "" : "/" ;
$subfolder .= end ( str_split ( $subfolder )) == "/" ? "" : "/" ;
// we start by going through all files in $folder
$handle = opendir ( $folder );
while ( $f = readdir ( $handle )) if ( $f != "." && $f != ".." ) if ( is_file ( $folder . $f )) // if we find a file, store it
// if we have a subfolder, store it there
if ( $subfolder != null )
$zipFile -> addFile ( $folder . $f , $subfolder . $f );
else
$zipFile -> addFile ( $folder . $f );
> elseif ( is_dir ( $folder . $f )) // if we find a folder, create a folder in the zip
$zipFile -> addEmptyDir ( $f );
// and call the function again
folderToZip ( $folder . $f , $zipFile , $f );
>
>
>
>
?>
Use it like this:
$z = new ZipArchive ();
$z -> open ( "test.zip" , ZIPARCHIVE :: CREATE );
folderToZip ( "storeThisFolder" , $z );
$z -> close ();
?>
Have a good day!
Here is a simple function which zips folders with all sub folders or only a simple file. the $data var can be a string or an array.
public function un_zip ( $data , $arcpf , $mode = 'zip' , $obj = '' ) <
$absoluterpfad = 'YOUR_BASE_PATH' ;
$arcpf = $absoluterpfad . DS . $arcpf ;
if( is_object ( $obj )== false ) <
$archiv = new ZipArchive ();
$archiv -> open ( $arcpf , ZipArchive :: CREATE );
>else < $archiv =& $obj ;>
if( $mode == 'zip' ) <
if( is_array ( $data )== true ) <
foreach( $data as $dtmp ) <
$archiv =& un_zip ( $dtmp , $arcpf , 'zip' ,& $archiv );
>
>else <
if( is_dir ( $data )== true ) <
$archiv -> addEmptyDir ( str_replace ( $absoluterpfad . DS , '' , $data ));
$files = scandir ( $data );
$bad = array( '.' , '..' );
$files = array_diff ( $files , $bad );
foreach( $files as $ftmp ) <
if( is_dir ( $data . DS . $ftmp )== true ) <
$archiv -> addEmptyDir ( str_replace ( $absoluterpfad . DS , '' , $data . '/' . $ftmp ));
$archiv =& un_zip ( $data . DS . $ftmp , $arcpf , 'zip' ,& $archiv );
>elseif( is_file ( $data . DS . $ftmp )== true ) <
$archiv -> addFile ( $data . DS . $ftmp , str_replace ( $absoluterpfad . DS , '' , $data . '/' . $ftmp ));
>
>
>elseif( is_file ( $data )== true ) < $archiv ->addFile ( $data , str_replace ( $absoluterpfad . DS , '' , $data ));>
>
>
if( is_object ( $obj )== false ) < $archiv ->close ();>
else
if( $mode == 'unzip' ) < $archiv ->extractTo ( $data );>
>
?>
Important: Due to the natural file size limit of 4GB (~3,6GB to be correct) of zip files, this class will generate corrupt files of the result is larger than 4 GB. Using tar.gz is a proper alternative.
Be wary that there are several algorithms to generate a zip file. I found that Office OpenXML files created with ZipArchive are not recognized by Excel 2007, for example.
You have to use a different class to zip in this case, such as PclZip.
You can check general purpose flag to test if the zip file is encrypted. Example function below.
/**
* Check if the file is encrypted
*
* Notice: if file doesn't exists or cannot be opened, function
* also return false.
*
* @param string $pathToArchive
* @return boolean return true if file is encrypted
*/
function isEncryptedZip ( $pathToArchive ) $fp = @ fopen ( $pathToArchive , 'r' );
$encrypted = false ;
if ( $fp && fseek ( $fp , 6 ) == 0 ) $string = fread ( $fp , 2 );
if ( false !== $string ) $data = unpack ( "vgeneral" , $string );
$encrypted = $data [ 'general' ] & 0x01 ? true : false ;
>
fclose ( $fp );
>
return $encrypted ;
>
How to detect corrupt files with CRC mismatch:
Creating a corrupt archive for testing is simple - zip some files and change a byte with a hex editor in the resulting ZIP file. Now you can test the file with a ZIP application to learn which file inside the archive is corrupt.
ZipArchive seems unable to detect broken files. ZipArchive::CHECKCONS doesn't help, only if it's not a ZIP file at all. It happily decompressed corrupt files in my tests and the user downloading the data is not informed.
You can simply verify on the server for smaller files:
$maxsize = 1024 * 1024 ;
$z = new ZipArchive ;
$r = $z -> open ( "foo.zip" , ZipArchive :: CHECKCONS );
if( $r !== TRUE )
die( 'ZIP error when trying to open "foo.zip": ' . $r );
$stat = $z -> statName ( "mybrokenfile.txt" );
if( $stat [ 'size' ] > $maxsize )
die( 'File too large, decompression denied' );
$s = $z -> getStream ( $file );
$data = stream_get_contents ( $s , $maxsize );
fclose ( $s );
if( $stat [ 'crc' ] != crc32 ( $data ))
die( 'File is corrupt!' );
//echo 'File is valid';
//you may send the file to the client now if you didn't output anything before
header ( 'Content-Description: File Transfer' );
header ( 'Content-Type: application/octet-stream' );
header ( 'Content-Disposition: attachment; filename="mybrokenfile.txt"' );
header ( 'Content-Transfer-Encoding: binary' );
header ( 'Content-Length: ' . $stat [ 'size' ]);
ob_clean ();
echo $data ;
$z -> close ();
?>
If the file shall not be fully decompressed on the server but decompressed while streaming to the client due to it's size, the file transfer already startet and printing an error message later doesn't work. Maybe the best way would be to interrupt the connection before closing the file transfer. The client should be able to detect this as corrupt download.
On the server side a function is needed, that can calculate the CRC32 on streamed data stepwise.
Возвращаемые значения
Возвращает true в случае успешного выполнения или false в случае возникновения ошибки.
Примеры
$zip = new ZipArchive ;
$res = $zip -> open ( 'test.zip' );
if ( $res === TRUE ) echo 'готово' ;
$zip -> extractTo ( 'test' );
$zip -> close ();
> else echo 'ошибка с кодом:' . $res ;
>
?>?php
$zip = new ZipArchive ;
$res = $zip -> open ( 'test.zip' , ZipArchive :: CREATE );
if ( $res === TRUE ) $zip -> addFromString ( 'test.txt' , 'содержимое файла' );
$zip -> addFile ( 'data.txt' , 'entryname.txt' );
$zip -> close ();
echo 'готово' ;
> else echo 'ошибка' ;
>
?>?php
$name = tempnam ( sys_get_temp_dir (), "FOO" );
$zip = new ZipArchive ;
$res = $zip -> open ( $name , ZipArchive :: OVERWRITE ); /* усечение, поскольку пустой файл недопустим */
if ( $res === TRUE ) $zip -> addFile ( 'data.txt' , 'entryname.txt' );
$zip -> close ();
echo 'готово' ;
> else echo 'ошибка' ;
>
?>?php
Список параметров
Имя ZIP-архива для открытия.
Сохранение в файл
Функция file_put_contents() записывает содержимое переменной в файл, если файла не существует. то пытается его создать, если существует то полностью перезапишет его.
File_put_contents:
Fopen / fwrite:
Набор функций fopen, fwrite, fclose предназначены для более гибкой работы с файлами.
- fopen – открытие или создание файла.
- fwrite – запись данных.
- fclose – закрытие файла.
Возможные режимы fopen():
Mode | Описание |
---|---|
r | Открывает файл только для чтения, помещает указатель в начало файла. |
r+ | Открывает файл для чтения и записи, помещает указатель в начало файла. |
w | Открывает файл только для записи, помещает указатель в начало файла и обрезает файл до нулевой длины. Если файл не существует – пробует его создать. |
w+ | Открывает файл для чтения и записи, помещает указатель в начало файла и обрезает файл до нулевой длины. Если файл не существует – пытается его создать. |
a | Открывает файл только для записи, помещает указатель в конец файла. Если файл не существует – пытается его создать. |
a+ | Открывает файл для чтения и записи, помещает указатель в конец файла. Если файл не существует – пытается его создать. |
x | Создаёт и открывает только для записи; помещает указатель в начало файла. Если файл уже существует, вызов fopen() закончится неудачей, вернёт false и выдаст ошибку. Если файл не существует, попытается его создать. |
x+ | Создаёт и открывает для чтения и записи, в остальном имеет то же поведение, что и « x ». |
c | Открывает файл только для записи. Если файл не существует, то он создаётся. Если же файл существует, то он не обрезается (в отличие от « w »), и вызов к этой функции не вызывает ошибку (также как и в случае с « x »). Указатель на файл будет установлен на начало файла. |
c+ | Открывает файл для чтения и записи, в остальном имеет то же поведение, что и « c ». |
Доступно в место fwrite() используют fputs() , разницы ни какой т.к. эта функция является псевдонимом.
Значение индекса последней добавленной записи (файл или каталог). Доступно с PHP 8.0.0 и PECL zip 1.18.0.
Статус Zip-архива. Доступно для закрытого архива, начиная с PHP 8.0.0 и PECL zip 1.18.0.
Системный статус Zip-архива. Доступно для закрытого архива, начиная с PHP 8.0.0 и PECL zip 1.18.0.
Количество файлов в архиве
Имя файла в файловой системе
Комментарий к архиву
Примечания
Замечание:
Файловые системы Windows NTFS не поддерживают некоторые символы в именах файлов, а именно <|>*?": . Имена файлов с точкой в конце также не поддерживаются. В отличие от некоторых инструментов извлечения, этот метод не поддерживает замену этих символов на подчёркивание, а вместо этого возникает ошибка при извлечении таких файлов.
Содержание
Список параметров
Место назначение, куда извлекать файлы.
Элементы для извлечения. Может принимать как одно значение, так и массив записей.
Читайте также: