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:
![alt text]()
Эта ошибка возникает как в IE, так и в Firefox в Windows. Он отлично работает на Mac. Кроме того, в Windows размер файла выглядит правильно:
![alt text]()
Изменить # 2. Эта проблема решена. См. Мой ответ ниже.
Ответы
Ответ 1
Хорошо, после долгих раздоров я понял проблему. Проблема исходит из следующей строки кода:
$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)));
Ответ 2
У меня была такая же проблема, и мое решение было похоже на правильный ответ на этот поток. Когда вы кладете файл в архив, вы не можете иметь абсолютные файлы (файлы, начинающиеся с косой черты), иначе они не будут открываться в 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
.
Ответ 3
Недавно у меня была аналогичная проблема, как вы описали. Я обнаружил, что ZipArchive в лучшем случае нестабилен.
Я решил свои проблемы с этой простой библиотекой
http://www.phpconcept.net/pclzip
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 по проблеме).
Ответ 4
Все эти предложения могут вам помочь, но в моем случае мне нужно написать 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;
Ответ 5
Использование специальных символов, таких как подчеркивание, вызывает проблемы, поскольку для ZipArchive требуются имена, закодированные в IBM850. См. Комментарии в онлайн-руководстве PHP: http://www.php.net/manual/en/function.ziparchive-addfile.php#95725.
Ответ 6
У меня были проблемы с этим раньше. Попробуйте снять заголовок типа содержимого. вот код, который я разработал для него, который работал в 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));
Ответ 7
В дополнение к тому, что предложили другие, важно обратить внимание на ваши имена файлов и каталогов, поскольку 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));
}
Ответ 8
У меня была та же проблема. Этот код работал у меня, но я НАШЕЛ В ПЕРВОЙ ЛИНИИ в моем 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);
Ответ 9
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
Это сработало для меня, когда я попробовал вышеуказанные решения.
Ответ 10
Я использовал отметку времени в zip файле. На самом деле файловая система Windows не поддерживает специальные символы, такие как ": \/*? < > |"
После удаления ":" из части времени он работает как шарм
Ответ 11
На всякий случай кто-то другой стучит по кирпичной стене в течение нескольких часов и страдает, как я. У меня была такая же проблема, и ни одно из решений не помогло, пока я не понял, что загружаю некоторые библиотеки в свой PHP, и у одной из них была пустая строка после кода? > . При вызове библиотеки с include(/path/to/lib/lib.php);
в браузер выводилась пустая строка, в результате чего zip был классифицирован как поврежденный Windows. (Winzip, Total Commander и т.д. Не было проблем с ним). Поэтому убедитесь, что нет импортированной библиотеки, или если она есть, у нее нет пробелов или пустых строк....
Ответ 12
У меня была эта проблема в течение часа. Попробовав 10 различных решений, я решил это, убедившись, что script существует после вывода ZIP файла:
readfile($zip_name);
unlink($zip_name);
**exit();**
Ответ 13
Для тех из вас, кто все еще стучит головой после попытки всего этого, и это все еще не работает, я смог решить мою проблему следующим образом.
$zipname = "download.zip";
$zip = new ZipArchive;
$zip->open($zipname, ZipArchive::CREATE);
foreach ($files as $file) {
# download file
$download_file = file_get_contents($file); <- important
#add it to the zip
$zip->addFromString(basename($file), $download_file); <- important
}
$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
которая сделала это для меня.
Ответ 14
Измените свой код:
header('Content-Length: '.filesize($strZipFilename), TRUE);
С
header('Content-Length: '.file_get_contents($strZipFilename));