Wordpress - Zip archive force download - php

I am trying to give the possiblity to an user to download an archive in wordpress, but the download don't start after the creation of the archive. I can see that my archive is created, she's available on the server. Here my code :
My "library"
<?php
function create_zip($files = array(), $destination = '', $overwrite = false) {
if(file_exists($destination) && !$overwrite) {
return false;
}
$valid_files = array();
if(is_array($files)) {
foreach($files as $file) {
if(file_exists($file)) {
$valid_files[] = $file;
}
}
}
if(count($valid_files)) {
$zip = new ZipArchive();
if($zip->open($destination,$overwrite ? ZIPARCHIVE::OVERWRITE : ZIPARCHIVE::CREATE) !== true) {
return false;
}
foreach($valid_files as $file) {
$zip->addFile($file,$file);
}
$zip->close();
return file_exists($destination);
} else {
return false;
}
}
?>
The function called by wordpress :
<?php
require "zip.php";
$customwp = plugins_url().'/customwp/';
wp_enqueue_style('tutoriels',$customwp.'css/tutoriels.css');
wp_enqueue_script('tutoriels',$customwp.'js/tutoriels.js',array('jquery'),'1.0',true);
ob_start();
$dir = ABSPATH . 'wp-content/plugins/customwp/';
$files_to_zip = array(
$dir.'zip.php'
);
$archive_name = "archive.zip";
$result = create_zip($files_to_zip, $archive_name);
header('Content-Transfer-Encoding: binary'); //Transfert en binaire (fichier).
header('Content-Disposition: attachment; filename="archive.zip"'); //Nom du fichier.
header('Content-Length: '.filesize($archive_name)); //Taille du fichier.
readfile($archive_name);
$output_string=ob_get_contents();
ob_end_clean();
return $output_string;
?>
If you can help me, don't hesitate to try !
Best regards

the final line, where you have
return $output_string;
it should be
echo $output_string;
also, although in this case it seems it doesn't affect your code, but keep in mind that output buffering doesn't buffer your header() calls, they are sent immediately. See the php page about it:
This function will turn output buffering on. While output buffering is active no output is sent from the script (other than headers), instead the output is stored in an internal buffer.

Related

ZipArchive sending corrupt files

I am using the ZipArchive class to create a ZIP file and stream it to the client but all archives are not generated correctly.
Any suggestions?
<?php
if(file_exists('../../../thure/uploads/nestingen/58861.pdf'))
{
$files = array('../../../thure/uploads/nestingen/58861.pdf');
$zipname = 'file.zip';
$zip = new ZipArchive;
$zip->open($zipname, ZipArchive::CREATE);
foreach ($files as $file) {
$zip->addFile($file);
}
$zip->close();
header('Content-Type: application/zip');
header('Content-disposition: attachment; filename='.$zipname);
header('Content-Length: ' . filesize($zipname));
readfile($zipname);
exit;
}
else
{
echo 'file not found';
}
?>
Function (writed by David)
/* creates a compressed zip file */
function create_zip($files = array(),$destination = '',$overwrite = false) {
//if the zip file already exists and overwrite is false, return false
if(file_exists($destination) && !$overwrite) { return false; }
//vars
$valid_files = array();
//if files were passed in...
if(is_array($files)) {
//cycle through each file
foreach($files as $file) {
//make sure the file exists
if(file_exists($file)) {
$valid_files[] = $file;
}
}
}
//if we have good files...
if(count($valid_files)) {
//create the archive
$zip = new ZipArchive();
if($zip->open($destination,$overwrite ? ZIPARCHIVE::OVERWRITE : ZIPARCHIVE::CREATE) !== true) {
return false;
}
//add the files
foreach($valid_files as $file) {
$zip->addFile($file,$file);
}
//debug
//echo 'The zip archive contains ',$zip->numFiles,' files with a status of ',$zip->status;
//close the zip -- done!
$zip->close();
//check to make sure the file exists
return file_exists($destination);
}
else
{
return false;
}
}
Sample Usage
$files_to_zip = array(
'file1',
'file2',
'file3'
);
//if true, good; if false, zip creation failed
$result = create_zip($files_to_zip,'my-archive.zip');

Zip->close() returns false despite checks

I'm trying to create a zip file with the ZipArchive class. I was reading a lot here and there and implemented a lot of checks (file existence, directory writable..). But zip->close() still returns 'false' and I can't figure out why.
if (!is_writable('../temp_downloads/')) {
die('directory not writable'); }
function create_zip($files = array(),$destination = '',$overwrite = false) {
$destination = "../temp_downloads/".$destination.".zip";
if(file_exists($destination) && !$overwrite) { return false; }
$valid_files = array();
if(is_array($files)) {
foreach($files as $file) {
if(file_exists('../data/'.$file) && is_readable('../data/'.$file)) {
$valid_files[] = $file;
}
}
}
if(count($valid_files)) {
$zip = new ZipArchive();
if($zip->open($destination,$overwrite ? ZIPARCHIVE::OVERWRITE : ZIPARCHIVE::CREATE) !== true) {
exit('cannot create zip');
}
foreach($valid_files as $file) {
$zip->addFile($file,$file); // result returns 'true'
}
$res = $zip->close(); //$res contains 'false'
if(file_exists($destination)){
error_log("zip exists ".$destination);
header("Content-type: application/zip");
header("Content-Disposition: attachment; filename=$destination");
header("Content-length: " . filesize($destination));
header("Pragma: no-cache");
header("Expires: 0");
readfile("$destination");
} else {
error_log("ERROR: Zip doesnt exist at ".$destination);
}
return file_exists($destination);
} else {
return false;
}
}
I call this function with
create_zip($files, 'PREFIX_'.$stamp, true);
where $files is an array and $stamp just a timestamp.
For testing the destination folders chmod is set to 777
Could you help me?
You're (maybe?) using incorrect paths for your files in the zip operations:
Here you look for ../data/$file:
if(file_exists('../data/'.$file) && is_readable('../data/'.$file)) {
$valid_files[] = $file;
But at zipping time, you have just:
$zip->addFile($file,$file);
instead of
$zip->addFile("../data/$file",$file);
I don't see how addFile could return true, unless you coincidentally happen to have duplicates of all those files in the script's current working directory, or you ARE working in a data directory and ../data is the same as .

php zip file not getting all images

I am trying to create a zip file of all the images that are stored in a database. This is done when the user clicks on a download button. Right now, you click on the download button, the zip file is created and the window pops up asking you if you want to open the file or save it to your computer. If you open the file, it shows you all the file names (like it's supposed to). The problem is, when you try to view the images, they are all the SAME image, just with different names.
Here's my code:
/* creates a compressed zip file */
function create_zip($files = array(), $destination = '', $overwrite = false) {
//if the zip file already exists and overwrite is false, return false
if(file_exists($destination) && !$overwrite) { return false; }
//vars
$path = 'admin/ReviewFiles/';
$valid_files = array();
//if files were passed in...
if(is_array($files)) {
//cycle through each file
foreach($files as $file) {
//make sure the file exists
$fullFile = $path . $file;
if(file_exists($fullFile)) {
$valid_files[] = $file;
}
}
}
//if we have good files...
if(count($valid_files)) {
//create the archive
$zip = new ZipArchive();
if($zip->open($destination,$overwrite ? ZIPARCHIVE::OVERWRITE : ZIPARCHIVE::CREATE) !== true) {
return false;
}
//add the files
foreach($valid_files as $file) {
$zip->addFile($file);
}
//close the zip -- done!
$zip->close();
//check to make sure the file exists
return file_exists($destination);
}
else
{
return false;
}
}
And here's where the function is called:
$i = 0;
$reviewFiles = array();
while ($row = mysql_fetch_object($result))
{
$reviewFiles[$i] = $row->fileName;
$i++;
}
$zipCreated = create_zip($reviewFiles, 'reviewFiles.zip', true);
$file = 'reviewFiles.zip';
if ($zipCreated) {
header('Content-Type: application/zip');
header('Content-Disposition: attachment; filename=reviewFiles.zip');
header('Expires: 0');
header('Pragma: no-cache');
readfile("reviewFiles.zip");
exit;
}
I have tried everything I can think of to figure out why it's not actually downloading each image. So, I guess clearly put, my question is: How can I fix the code to make it actually zip each file, not just one image with the same image with different names?
Thanks!
This:
if(file_exists($fullFile)) {
$valid_files[] = $file;
}
should be:
if(file_exists($fullFile)) {
$valid_files[] = $fullFile;
}
Otherwise the files could not be found. If you have changed this, you may also wish to set a local name:
foreach($valid_files as $file) {
$zip->addFile($file, basename($file));
}
I'm using basename() to prevent zip from creating sub directories in the archive.

setting a php var to * (all)

Im using the bellow code to create and download a zip file from all the files in the root of the folder.
Ive got this line
$files_to_zip = array(
'room1.jpg', 'room2.jpg'
);
which lets you chose which files to put into the zip file - but my files are the result of a cron job so theres a new one generated each day. How can i write a variable to say all files, apart from " " and then list the files that i dont want in the zipfolder ie. index.php ect..
so far ive tried writing $files_to_zip = *; (obviously that would not exclude any files) but that just throws up a Parse error: syntax error
this is the full code bellow:
<?php
/* creates a compressed zip file */
function create_zip($files = array(),$destination = '',$overwrite = true) {
//if the zip file already exists and overwrite is false, return false
if(file_exists($destination) && !$overwrite) { return false; }
//vars
$valid_files = array();
//if files were passed in...
if(is_array($files)) {
//cycle through each file
foreach($files as $file) {
//make sure the file exists
if(file_exists($file)) {
$valid_files[] = $file;
}
}
}
//if we have good files...
if(count($valid_files)) {
//create the archive
$zip = new ZipArchive();
if($zip->open($destination,$overwrite ? ZIPARCHIVE::OVERWRITE : ZIPARCHIVE::CREATE) !== true) {
return false;
}
//add the files
foreach($valid_files as $file) {
$zip->addFile($file,$file);
}
//debug
//echo 'The zip archive contains ',$zip->numFiles,' files with a status of ',$zip->status;
//close the zip -- done!
$zip->close();
//check to make sure the file exists
return file_exists($destination);
}
else
{
return false;
}
}
$files_to_zip = array(
'room1.jpg', 'room2.jpg'
);
//if true, good; if false, zip creation failed
$zip_name = 'my-archive.zip';
$result = create_zip($files_to_zip,$zip_name);
if($result){
header('Content-Type: application/zip');
header('Content-disposition: attachment; filename=filename.zip');
header('Content-Length: ' . filesize($zip_name));
readfile($zip_name);
}
?>
The function to do wildcard matching against the filesystem is glob().
$files_to_zip = glob("*");

Zipped file with PHP results in cpgz file after extraction

I'm zipping folders and files with php, but when I try to open up the zip file I get an cpgz file instead. After extracting that file I get another zip file. What it does is bassicaly scan the current folder for files and folders to zip.
This is the code I use:
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('\\', '/', realpath($file));
if (is_dir($file) === true)
{
$zip->addEmptyDir(str_replace($source . '/', '', $file . '/'));
}
else if (is_file($file) === true)
{
$zip->addFromString(str_replace($source . '/', '', $file), file_get_contents($file));
}
}
}
else if (is_file($source) === true)
{
$zip->addFromString(basename($source), file_get_contents($source));
}
return $zip->close();
}
if($_GET["archive"]== 'true'){
$date = date("Ymd_Hi");
$dir = dirname(__FILE__);
$filename = $date.".zip";
Zip(getcwd(), $filename);
header("Content-disposition: attachment; filename=$filename");
header('Content-type: application/zip');
readfile($filename);
unlink($filename);
}
I was just having exactly the same issue, and learned that these two function calls can help:
header("Content-disposition: attachment; filename=$filename");
header('Content-type: application/zip');
// Add these
ob_clean();
flush();
readfile($filename);
unlink($filename);
Usually setting Content-Disposition and Content-Length should be enough, however flushing PHPs output buffer and the underlying output buffer (Apache or such) helps when output is accidentally sent before headers are set in PHP.
In my case, commenting out the header() and readfile() calls for debugging helped to see, that warnings were output before the file was sent.
Hope that helps somebody in the future.

Categories