PHP ZipArchive поврежден в Windows
Я использую класс PHP ZipArchive для создания zip файла, содержащего фотографии, и затем загружаю его в браузер для загрузки. Вот мой код:
/** * Grabs the order, packages the files, and serves them up for download. * * @param string $intEntryID * @return void * @author Jesse Bunch */ public static function download_order_by_entry_id($intUniqueID) < $objCustomer = PhotoCustomer::get_customer_by_unique_id($intUniqueID); if ($objCustomer): if (!class_exists('ZipArchive')): trigger_error('ZipArchive Class does not exist', E_USER_ERROR); endif; $objZip = new ZipArchive(); $strZipFilename = sprintf('%s/application/tmp/%s-%s.zip', $_SERVER['DOCUMENT_ROOT'], $objCustomer->getEntryID(), time()); if ($objZip->open($strZipFilename, ZIPARCHIVE::CREATE) !== TRUE): trigger_error('Unable to create zip archive', E_USER_ERROR); endif; foreach($objCustomer->arrPhotosRequested as $objPhoto): $filename = PhotoCart::replace_ee_file_dir_in_string($objPhoto->strHighRes); $objZip->addFile($filename,sprintf('/press_photos/%s-%s', $objPhoto->getEntryID(), basename($filename))); endforeach; $objZip->close(); header('Last-Modified: '.gmdate('D, d M Y H:i:s', filemtime($strZipFilename)).' GMT', TRUE, 200); header('Cache-Control: no-cache', TRUE); header('Pragma: Public', TRUE); header('Expires: ' . gmdate('D, d M Y H:i:s', time()) . ' GMT', TRUE); header('Content-Length: '.filesize($strZipFilename), TRUE); header('Content-disposition: attachment; filename=press_photos.zip', TRUE); header('Content-Type: application/octet-stream', TRUE); ob_start(); readfile($strZipFilename); ob_end_flush(); exit; else: trigger_error('Invalid Customer', E_USER_ERROR); endif; >
Этот код отлично работает со всеми браузерами, но IE. В IE файл загружается правильно, но zip-архив пуст. При попытке извлечь файлы Windows сообщает мне, что zip-архив поврежден. Кто-нибудь имел эту проблему раньше?
Изменить Обновление: после предложения от @profitphp я изменил свои заголовки на следующее:
header("Cache-Control: public"); header("Pragma: public"); header("Expires: 0"); header("Cache-Control: must-revalidate, post-check=0, pre-check=0"); header("Cache-Control: public"); //header("Content-Description: File Transfer"); //header("Content-type: application/zip"); header("Content-Disposition: attachment; filename=\"pressphotos.zip\""); //header("Content-Transfer-Encoding: binary"); header("Content-length: " . filesize($strZipFilename));
Кроме того, вот скриншот ошибки в Windows после открытия с помощью Firefox:
Эта ошибка возникает как в IE, так и в Firefox в Windows. Он отлично работает на Mac. Кроме того, в Windows размер файла выглядит правильно:
Изменить # 2. Эта проблема решена. См. Мой ответ ниже.
Хорошо, после долгих раздоров я понял проблему. Проблема исходит из следующей строки кода:
$objZip->addFile($filename,sprintf('/press_photos/%s-%s', $objPhoto->getEntryID(), basename($filename)));
По какой-то причине часть /press_photos/ этого пути для локального (внутреннего) имени файла внутри zip-архива приводила к тому, что Windows считала, что zip файл поврежден. После изменения строки, чтобы выглядеть так, как показано ниже, Windows корректно открыла zip файлы. Уф.
$objZip->addFile($filename,sprintf('%s-%s', $objPhoto->getEntryID(), basename($filename)));
У меня была такая же проблема, и мое решение было похоже на правильный ответ на этот поток. Когда вы кладете файл в архив, вы не можете иметь абсолютные файлы (файлы, начинающиеся с косой черты), иначе они не будут открываться в Windows по какой-либо причине.
Поэтому он работал не потому, что он (Джесси Бунч, выбранный ответ на момент написания этой статьи) удалил содержащую папку, но потому, что он удалил стартовую косую черту.
Я исправил проблему, изменив
$zip->addFile($file, $file); // $file is something like /path/to/file.png
// we make file relative by removing beginning slash so it will open in Windows $zip->addFile($file, ltrim($file, '/'));
а затем он смог открыть в Windows!
Вероятно, та же самая причина pclzip (ответ Plahcinski). Держу пари, он автоматически отбрасывает начальную косую черту.
Я бы не понял этого без конкретного комментария на странице документации PHP ZipArchive::addFile .
Недавно у меня была аналогичная проблема, как вы описали. Я обнаружил, что ZipArchive в лучшем случае нестабилен.
Я решил свои проблемы с этой простой библиотекой
include_once('libs/pclzip.lib.php');
function zip($source, $destination)< $zipfile = new PclZip($destination); $v_list = $zipfile->create($source, '', $source); >
$source = папка, которую я хотел бы закрепить
$ destination = расположение файла zip
Я потратил 2 дня на ZipArchive, а затем решил все проблемы с PCLZip за 5 минут.
Надеюсь, это поможет вам и всем, у кого есть эта проблема (так как это близко к верхнему результату Google по проблеме).
Все эти предложения могут вам помочь, но в моем случае мне нужно написать ob_clean(); перед первым заголовком (”); потому что некоторый файл, который я включаю перед печатью некоторых символов, которые разбивают файл zip на окна.
$zip=new ZipArchive(); $zip->open($filename, ZIPARCHIVE::CREATE); $zip->addFile($file_to_attach,$real_file_name_to_attach); $zip->close(); ob_clean(); header("Cache-Control: no-cache, must-revalidate"); // HTTP/1.1 header("Expires: Sat, 26 Jul 1997 05:00:00 GMT"); // Date in the past header('Content-Type: application/x-download'); header('Content-Disposition: attachment; filename="file.zip"'); readfile($filename); exit;
Использование специальных символов, таких как подчеркивание, вызывает проблемы, поскольку для ZipArchive требуются имена, закодированные в IBM850. См. Комментарии в онлайн-руководстве PHP: http://www.php.net/manual/en/function.ziparchive-addfile.php#95725.
У меня были проблемы с этим раньше. Попробуйте снять заголовок типа содержимого. вот код, который я разработал для него, который работал в IE и FF. Обратите внимание на прокомментированные строки, которые имеют одинаковые проблемы с разными комбо из тех, кто находится.
header("Cache-Control: public"); header("Pragma: public"); header("Expires: 0"); header("Cache-Control: must-revalidate, post-check=0, pre-check=0"); header("Cache-Control: public"); //header("Content-Description: File Transfer"); //header("Content-type: application/zip"); header("Content-Disposition: attachment; filename=\"adwords-csv.zip\""); //header("Content-Transfer-Encoding: binary"); header("Content-length: " . filesize($filename));
В дополнение к тому, что предложили другие, важно обратить внимание на ваши имена файлов и каталогов, поскольку Windows не обязательно должна соответствовать пути и имена файла Linux. Иногда он также ускользает от них при застегивании. Примеры многочисленны, но самое главное
- * dot files (. и..), файлы с разницей только в случае (name.txt и NAME.txt),
- абсолютные пути к файлу (/tmp/file.txt)*.
- Некоторые другие символы, разрешенные в именах файлов в Windows, могут вызвать проблемы, когда проводник Windows используется для открытия файлов. В моем случае символ “:” был нарушителем сделки, но он много работал, чтобы найти это.
Итак, прежде чем переходить к использованию с использованием большого количества параметров с помощью exec (‘zip…’), я предлагаю простую процедуру:
- Найдите папку или файл, который ваш сайт застегнет.
- run: zip -9 -r -k zip-modified-names.zip/path/to/your/folder
- обратите внимание на то, что выжимает консоль. В моем случае “:” в именах файлов были удалены.
- Переместите zip файл на компьютер Windows и попытайтесь открыть его.
Если это работает, вам может быть лучше удалить символы, которые были удалены опцией -k из ваших имен файлов/каталогов, попробуйте обычную застежку. Обратите внимание, что некоторые параметры, такие как -k, имеют побочные эффекты. В этом случае -k противоречит опции -q (для символических ссылок).
Также опция -k может сделать ваши имена файлов нечитаемыми. В моем случае мои файлы были названы на основе времени создания (например, 10: 55: 39.pdf), чтобы облегчить поиск требуемой записи из архивов, но опция -k превратила ее в 105539.pdf, которую пользователи не легко читают. Поэтому я изменил имена на 10_55_39.pdf, который открывается в Windows без использования опции -k, но по-прежнему доступен для чтения.
В дополнение к этому использование PCLZip сделает вашу жизнь намного проще, поскольку вы можете сразу добавить всю папку, а также изменить путь к файлам в одной простой строке. В моем случае я удаляю/tmp/directory/из моих zip файлов со вторым и третьим параметрами, которые позволяют избежать другой проблемы совместимости с Windows (с абсолютным путем в zip файлах):
$v_list = $zip->create( $sourceFolder, PCLZIP_OPT_REMOVE_PATH, $sourceFolder . DIRECTORY_SEPARATOR); if ($v_list == 0) < throw new \Exception($zip->errorInfo(true)); >
У меня была та же проблема. Этот код работал у меня, но я НАШЕЛ В ПЕРВОЙ ЛИНИИ в моем php файле! Если я поставил код в середине файла, я не работал. Возможно, некоторые проблемы с кодировкой?!
// Prepare File $file = tempnam("tmp", "zip"); $zip = new ZipArchive(); $zip->open($file, ZipArchive::OVERWRITE); // Staff with content $zip->addFile($filepathOnServer, 'mypic.jpg'); // Close and send to users $zip->close(); header('Content-Type: application/zip'); header('Content-Disposition: attachment; filename="filename.zip"'); readfile($file); unlink($file);
ob_clean(); //very important // http headers for zip downloads header("Pragma: public"); header("Expires: Sat, 26 Jul 1997 05:00:00 GMT"); // Date in the past header("Cache-Control: no-cache, must-revalidate"); // HTTP/1.1 header("Cache-Control: public"); header('Content-Type: application/x-download'); header("Content-Disposition: attachment; filename=\"filename.zip\""); header("Content-Length: ".filesize($filepath )); @readfile($filepath ); unlink($filepath); //very important exit;//very important
Это сработало для меня, когда я попробовал вышеуказанные решения.
Я использовал отметку времени в zip файле. На самом деле файловая система Windows не поддерживает специальные символы, такие как “: \/*? < >|”
После удаления “:” из части времени он работает как шарм
На всякий случай кто-то другой стучит по кирпичной стене в течение нескольких часов и страдает, как я. У меня была такая же проблема, и ни одно из решений не помогло, пока я не понял, что загружаю некоторые библиотеки в свой PHP, и у одной из них была пустая строка после кода? > . При вызове библиотеки с include(/path/to/lib/lib.php); в браузер выводилась пустая строка, в результате чего zip был классифицирован как поврежденный Windows. (Winzip, Total Commander и т.д. Не было проблем с ним). Поэтому убедитесь, что нет импортированной библиотеки, или если она есть, у нее нет пробелов или пустых строк….
У меня была эта проблема в течение часа. Попробовав 10 различных решений, я решил это, убедившись, что script существует после вывода ZIP файла:
readfile($zip_name); unlink($zip_name); **exit();**
Для тех из вас, кто все еще стучит головой после попытки всего этого, и это все еще не работает, я смог решить мою проблему следующим образом.
$zipname = "download.zip"; $zip = new ZipArchive; $zip->open($zipname, ZipArchive::CREATE); foreach ($files as $file) < # download file $download_file = file_get_contents($file); addFromString(basename($file), $download_file); $zip->close(); header('Content-Type: application/zip'); //header('Content-disposition: attachment; filename='.$zipname); header("Content-Disposition: attachment; filename=\"$zipname\""); header('Content-Length: ' . filesize($zipname)); readfile($zipname); unlink($zipname); exit;
Моя проблема заключалась в том, что мои пути к файлам входили в виде строк. Даже при том, что я попытался (string)$path это все еще не работало. Для меня это было использование file_get_contents файла, а затем встроенная функция addFromString которая сделала это для меня.
header('Content-Length: '.filesize($strZipFilename), TRUE);
header('Content-Length: '.file_get_contents($strZipFilename));