PHP: упаковываем и распаковываем файлы zip на сервере
Неудавшаяся попытка скрипта, запишем для истории :) Сама по себе работа с классом ZipArchive ничего особенного не представляет, проблемы начинаются, когда в именах файлов есть символы национальных алфавитов.
При архивировании особых проблем не возникло. HTML-форма и браузер считают, что имя файла передано в Юникоде, скрипт тоже должен быть сохранён в Юникоде (UTF-8), тогда при упаковке браузер корректно отдаёт файл с именем archive.zip
. Если же кириллицу содержит исходное имя архивируемого файла, то заменяем её на латиницу функцией cyr2lat
.
При распаковке архивов начинаются проблемы. В формате ZIP имена файлов хранятся в однобайтовой кодировке, в связи с чем возникает известный баг PHP, при котором разархивация "портит" имена извлекаемых файлов, хотя методы getNameIndex
и extractTo
из кода работают.
Как исправить баг с именами - показано в коде, но попытка переименовать файлы из архива на "правильные" имена с помощью метода renameName
всё равно не удалась, так что это не решение, а только набросок, который возможно, пригодится в будущем.
Также не решена проблема, каким образом получить правильные HTTP-заголовки, чтобы "отдать" браузеру сразу кучу файлов (все файлы архива) за одну транзакцию (см. функцию file_download
в коде и её закомментаренный вызов). В реальном приложении хранить на сервере множество загруженных юзером файлов, конечно же, не стоит, нужно отдавать файлы браузеру и удалять их с сервера. Скрипт в том состоянии, что выложен, просто выводит ссылки на загруженные файлы, причём, не факт, что они сработают - именно из-за "испорченных" при извлечении классом ZipArchive
имён файлов.
Для краткости также не приведено HTML-обрамление, предполагается, что скрипт размещён на хосте в отдельной папке с файлом .htaccess
, устанавливающим кодировку UTF-8:
AddDefaultCharset utf-8
и созданной вложенной папкой unpacked
для загружаемых файлов:
вид папок скрипта на хосте
Вот листинг файла index.php:
<?php if (empty($_POST['action'])) { echo ' <form method="POST" action="" enctype="multipart/form-data"> <input type="radio" name="action" value="unzip"/>Распаковать <input type="radio" name="action" value="zip"/>Запаковать <br/> <input type="file" name="filename"/> <br/> <input type="submit" value="Выполнить" /> </form> '; return; } define ('ARCHIVE_FOLDER','./unpacked/'); $error = ''; if (!class_exists('ZipArchive')) { $error = 'Извините, ваша версия PHP не поддерживает работу с архивами ZIP'; } else { if ($_FILES['filename']['error'] != UPLOAD_ERR_OK) { $error = 'Системная ошибка работы с файлом: '.$_FILES['filename']['error']; } else { $zip = new ZipArchive; if (isset($_POST['action']) and $_POST['action']=='unzip') { if ($zip->open($_FILES['filename']['tmp_name']) === true) { $filenames = Array(); for ($i=0; $i<$zip->numFiles; $i++) { $filenames[] = $zip->getNameIndex ($i); //https://bugs.php.net/bug.php?id=65815 if ($filenames[$i] === mb_convert_encoding( mb_convert_encoding($filenames[$i], "UTF-32", "UTF-8"), "UTF-8","UTF-32")) ; //nothing to do else $filenames[$i] = mb_convert_encoding ($filenames[$i],'UTF-8','CP866'); //Don't work: //$zip->renameName ($zip->getNameIndex($i),$filenames[$i]); } if (count($filenames)<1) { $error = 'В архиве '.$_FILES['filename']['name'].' не обнаружено ни одного файла'; } else { if ($zip->extractTo (ARCHIVE_FOLDER)!=true) { $error = 'Не удалось извлечь файлы в папку '.ARCHIVE_FOLDER; } else { echo '<p>'; for ($i=0; $i<count($filenames); $i++) { echo '<br><a href="'.ARCHIVE_FOLDER.$zip->getNameIndex($i).'">'.$filenames[$i].'</a>'; //file_download (ARCHIVE_FOLDER.$filenames[$i]); //@unlink(ARCHIVE_FOLDER.$filenames[$i]); } echo '</p>'; } } $zip->close(); } else { $error = 'Не могу открыть архив с именем '.$_FILES['filename']['name']; } } else if (isset($_POST['action']) and $_POST['action']=='zip') { $filename = ARCHIVE_FOLDER.'archive.zip'; if ($zip->open($filename, ZipArchive::CREATE) === true) { $zip->addFile ($_FILES['filename']['tmp_name'],cyr2lat($_FILES['filename']['name'])); $zip->close(); header('Content-type: application/zip; name=archive.zip'); header("Content-Transfer-Encoding: Binary"); header("Content-Length: ".filesize($filename)); header("Content-Disposition: attachment; filename=\"".basename($filename)."\""); readfile ($filename); //отдать файл браузеру @unlink($filename); //удалить архив с сервера } else { $error = 'Невозможно создать архив на сервере'; } } @unlink ($_FILES['filename']['tmp_name']); } } print_r ($error);//!!! if (!empty($error)) echo '<p>'.$error.'; <a href="index.php">Назад</a></p>'; function cyr2lat ($text) { $cyr2lat_replacements = array ( "А" => "a","Б" => "b","В" => "v","Г" => "g","Д" => "d", "Е" => "e","Ё" => "yo","Ж" => "dg","З" => "z","И" => "i", "Й" => "y","К" => "k","Л" => "l","М" => "m","Н" => "n", "О" => "o","П" => "p","Р" => "r","С" => "s","Т" => "t", "У" => "u","Ф" => "f","Х" => "h","Ц" => "ts","Ч" => "ch", "Ш" => "sh","Щ" => "csh","Ъ" => "","Ы" => "i","Ь" => "", "Э" => "e","Ю" => "yu","Я" => "ya", "а" => "a","б" => "b","в" => "v","г" => "g","д" => "d", "е" => "e","ё" => "yo","ж" => "dg","з" => "z","и" => "i", "й" => "y","к" => "k","л" => "l","м" => "m","н" => "n", "о" => "o","п" => "p","р" => "r","с" => "s","т" => "t", "у" => "u","ф" => "f","х" => "h","ц" => "ts","ч" => "ch", "ш" => "sh","щ" => "sch","ъ" => "","ы" => "i","ь" => "", "э" => "e","ю" => "yu","я" => "ya", "-" => "_"," " => "_" ); return strtr ($text,$cyr2lat_replacements); } function file_download ($file) { if (file_exists ($file) and is_file($file)) { if (ob_get_level()) ob_end_clean(); header('Content-Description: File Transfer'); header('Content-Type: application/octet-stream'); header('Content-Disposition: attachment; filename='.basename($file)); header('Content-Transfer-Encoding: binary'); header('Expires: 0'); header('Cache-Control: must-revalidate'); header('Pragma: public'); header('Content-Length: ' . filesize($file)); readfile($file); } } ?>
вид формы скрипта
15.03.2017, 11:05 [6129 просмотров]