Прежде чем отмечать этот пост как дубликат, обратите внимание, что я уже искал ответ на SO, и тот раз, что я нашел до сих пор (перечисленные ниже), не был именно тем, что я искал.
Это лишь некоторые из тех, на которые я смотрел.
Моя проблема заключается в следующем: я не могу использовать addFromString, мне нужно использовать addFile, это требование задачи.
Я уже пробовал пару способов, вот моя текущая итерация:
public function getZippedFiles($path) { $real_path = WEBROOT_PATH.$path; $files = new RecursiveIteratorIterator (new RecursiveDirectoryIterator($real_path), RecursiveIteratorIterator::LEAVES_ONLY); //# create a temp file & open it $tmp_file = tempnam($real_path,''); $zip_file = preg_replace('"\.tmp$"', '.zip', $tmp_file); $zip = new ZipArchive(); $zip->open($zip_file, ZipArchive::CREATE); foreach ($files as $name=>$file) { error_log(print_r($name, true)); error_log(print_r($file, true)); if ( ($file == ".") || ($file == "..") ) { continue; } $file_path = $file->getRealPath(); $zip->addFile($file_path); } $zip->close(); }
Когда я пытаюсь открыть результирующий файл, мне говорят, что «Windows не может открыть папку. Сжатая (застегнутая молния) папка» недопустима ».
Мне удалось успешно выполнить задачу с помощью addFromString, например:
$file_path = WEBROOT_PATH.$path; $files = array(); if (is_dir($file_path) == true) { if ($handle = opendir($file_path)) { while (($file = readdir($handle)) !== false) { if (is_dir($file_path.$file) == false) { $files[] = $file_path."\\".$file; } } //# create new zip opbject $zip = new ZipArchive(); //# create a temp file & open it $tmp_file = tempnam($file_path,''); $zip_file = preg_replace('"\.tmp$"', '.zip', $tmp_file); $zip->open($zip_file, ZipArchive::CREATE); //# loop through each file foreach($files as $file){ //# download file $download_file = file_get_contents($file); //#add it to the zip $zip->addFromString(basename($file),$download_file); } //# close zip $zip->close(); } } }
Выше всего просто скопировано прямо из некоторого примера кода, который я где-то видел. Если кто-то может указать мне в хорошем направлении, я был бы очень благодарен!
***** UPDATE ***** Я добавил, если вокруг:
if (!$zip->close()) { echo "failed writing zip to archive"; }
Сообщение получает эхо, поэтому очевидно, что проблема есть. Я также проверил, что работает $zip->open()
, и я подтвердил, что он открывает его без проблем.
Наконец удалось получить что-то работающее с помощью addFile
.
Я создал вспомогательный класс, который содержит 3 функции: один для перечисления всех файлов в каталоге, 1 для zip всех этих файлов и 1 для загрузки заархивированных файлов:
<?php require_once($_SERVER["DOCUMENT_ROOT"]."/config.php"); class FileHelper extends ZipArchive { /** * Lists files in dir * * This function expects an absolute path to a folder intended as the target. * The function will attempt to create an array containing the full paths to * all files in the target directory, except for . and .. * * @param dir [string] : absolute path to the target directory * * @return result [array] : array of absolute paths pointing to files in the target directory */ public static function listDirectory($dir) { $result = array(); $root = scandir($dir); foreach($root as $value) { if($value === '.' || $value === '..') { continue; } if(is_file("$dir$value")) { $result[] = "$dir$value"; continue; } if(is_dir("$dir$value")) { $result[] = "$dir$value/"; } foreach(self::listDirectory("$dir$value/") as $value) { $result[] = $value; } } return $result; } /** * Zips and downloads files * * This function expects a directory location as target for a temp file, and a list(array) * of absolute file names that will be compressed and added to a zip file. After compression, * the temporary zipped file will be downloaded and deleted. * * @param location [string] : absolute path to the directory that will contain the temp file * @param file_list [array] : array of absolute paths pointing to files that need to be compressed * * @return void */ public function downloadZip($file) { $modules = apache_get_modules(); if (in_array('mod_xsendfile', $modules)) // Note, it is not possible to detect if X-SendFile is turned on or not, we can only check if the module is installed. If X-SendFile is installed but turned off, file downloads will not work { header("Content-Type: application/octet-stream"); header('Content-Disposition: attachment; filename="'.basename($file).'"'); header("X-Sendfile: ".realpath(dirname(__FILE__)).$file); // Apache will take care of the rest, so terminate the script exit; } header("Content-Type: application/octet-stream"); header("Content-Length: " .(string)(filesize($file)) ); header('Content-Disposition: attachment; filename="'.basename($file).'"'); header("Content-Transfer-Encoding: binary"); header("Expires: 0"); header("Cache-Control: no-cache, must-revalidate"); header("Cache-Control: private"); header("Pragma: public"); ob_end_clean(); // Without this, the file will be read into the output buffer which destroys memory on large files readfile($file); } /** * Zips files * * This function expects a directory location as target for a temp file, and a list(array) * of absolute file names that will be compressed and added to a zip file. * * @param location [string] : absolute path to the directory that will contain the temp file * @param file_list [array] : array of absolute paths pointing to files that need to be compressed * * @return zip_file [string] : absolute file path of the freshly zipped file */ public function zipFile($location, $file_list) { $tmp_file = tempnam($location,''); $zip_file = preg_replace('"\.tmp$"', '.zip', $tmp_file); $zip = new ZipArchive(); if ($zip->open($zip_file, ZIPARCHIVE::CREATE) === true) { foreach ($file_list as $file) { if ($file !== $zip_file) { $zip->addFile($file, substr($file, strlen($location))); } } $zip->close(); } // delete the temporary files unlink($tmp_file); return $zip_file; } } ?>
Вот как я назвал функции этого класса:
$location = "d:/some/path/to/file/"; $file_list = $file_helper::listDirectory($location); $zip_file = $file_helper->zipFile($location, $file_list); $file_helper->downloadZip($zip_file);
Проверьте это решение, гораздо яснее:
function Zip($source, $destination) { if (!extension_loaded('zip') || !file_exists($source)) { return false; } $zip = new ZipArchive(); if (!$zip->open($destination, ZIPARCHIVE::CREATE)) { return false; } $source = str_replace('\\', '/', realpath($source)); if (is_dir($source) === true) { $files = new RecursiveIteratorIterator(new RecursiveDirectoryIterator($source), RecursiveIteratorIterator::SELF_FIRST); foreach ($files as $file) { $file = str_replace('\\', '/', $file); // Ignore "." and ".." folders if( in_array(substr($file, strrpos($file, '/')+1), array('.', '..')) ) continue; $file = realpath($file); if (is_dir($file) === true) { $zip->addEmptyDir(str_replace($source . '/', '', $file . '/')); } else if (is_file($file) === true) { $zip->addFile($file, str_replace($source . '/', '', $file)); } } } else if (is_file($source) === true) { $zip->addFile($file, str_replace($source . '/', '', $file)); } return $zip->close(); }
И используйте его как:
Zip('/folder/to/compress/', './compressed.zip');
Проверьте исходное сообщение с помощью addFromString