This question already has answers here:
How to [recursively] Zip a directory in PHP? [duplicate]
(12 answers)
Closed 9 years ago.
//Get the directory to zip
$filename_no_ext=$_GET['directtozip'];
// we deliver a zip file
header("Content-Type: archive/zip");
// filename for the browser to save the zip file
header("Content-Disposition: attachment; filename=$filename_no_ext".".zip");
// get a tmp name for the .zip
$tmp_zip = tempnam ("tmp", "tempname") . ".zip";
//change directory so the zip file doesnt have a tree structure in it.
chdir('uploads/'.$_GET['directtozip']);
// zip the stuff (dir and all in there) into the tmp_zip file
exec('zip '.$tmp_zip.' *');
// calc the length of the zip. it is needed for the progress bar of the browser
header('Content-Length: ' . filesize($file));
// deliver the zip file
$fp = fopen("$tmp_zip","r");
echo fpassthru($fp);
// clean up the tmp zip file
unlink($tmp_zip);
This following code creates me blank zip files.
This is my ip localhostfilemanager/zip_folder.php?directtozip=Screenshots
Directory tree
Screenshots
- Image.jpg
Uploads
^Screenshots
- Image.jpg
And it basically doesn't get any of those files. Why is that? I search recently nearly all codes in google and the codes which worked wasn't based with header output just creating the zip in a directory ./ . Could you provide me with a working code im hopeless :(
copy all your files into a temp location then use this to create a zip file of your temp folder then delete your temp folder
/**
* Function will recursively zip up files in a directory and all sub directories / files in the specified source
* #param - $source - directory that you want contents of zipping - note does NOT zip primary directory only files and folders within directory
* #param - $destination - filepath and filename you are storing your created zip files in (could also be used to stream files down using the correct stream headers) eg: "/createdzips/zippy.zip"
* #return nothing - nada - null - zero - zilch - zip :)
*/
function zipcreate($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();
}
zipcreate("c:/xampp/htdocs/filemanager/Screenshots", "c:/xampp/htdocs/filemanager/uploads/screenshots.zip");
header("Content-Description: File Transfer");
header("Content-type: application/octet-stream");
header("Content-Disposition: attachment; filename=\"c:/xampp/htdocs/filemanager/uploads/screenshots.zip\"");
header("Content-Transfer-Encoding: binary");
header("Content-Length: ".filesize("c:/xampp/htdocs/filemanager/uploads/screenshots.zip"));
Why not try the ZipArchive library
<?php
$zip = new ZipArchive;
$filename = "text.zip";
$filepath = "path/to/zip";
if ($zip->open('test.zip') === TRUE) {
$zip->addFile('/path/to/index.txt', 'newname.txt');
$zip->close();
header("Content-Description: File Transfer");
header("Content-type: application/octet-stream");
header("Content-Disposition: attachment; filename=\".$filename."\");
header("Content-Transfer-Encoding: binary");
header("Content-Length: ".filesize($filepath.$filename));
} else {
echo 'failed';
}
?>
Its older but for what you're trying to do, it is much cleaner.
Zip a folder (include itself).
Usage:
HZip::zipDir('/path/to/sourceDir', '/path/to/out.zip');
<?php
class HZip
{
/**
* Add files and sub-directories in a folder to zip file.
* #param string $folder
* #param ZipArchive $zipFile
* #param int $exclusiveLength Number of text to be exclusived from the file path.
*/
private static function folderToZip($folder, &$zipFile, $exclusiveLength) {
$handle = opendir($folder);
while (false !== $f = readdir($handle)) {
if ($f != '.' && $f != '..') {
$filePath = "$folder/$f";
// Remove prefix from file path before add to zip.
$localPath = substr($filePath, $exclusiveLength);
if (is_file($filePath)) {
$zipFile->addFile($filePath, $localPath);
} elseif (is_dir($filePath)) {
// Add sub-directory.
$zipFile->addEmptyDir($localPath);
self::folderToZip($filePath, $zipFile, $exclusiveLength);
}
}
}
closedir($handle);
}
/**
* Zip a folder (include itself).
* Usage:
* HZip::zipDir('/path/to/sourceDir', '/path/to/out.zip');
*
* #param string $sourcePath Path of directory to be zip.
* #param string $outZipPath Path of output zip file.
*/
public static function zipDir($sourcePath, $outZipPath)
{
$pathInfo = pathInfo($sourcePath);
$parentPath = $pathInfo['dirname'];
$dirName = $pathInfo['basename'];
$z = new ZipArchive();
$z->open($outZipPath, ZIPARCHIVE::CREATE);
$z->addEmptyDir($dirName);
self::folderToZip($sourcePath, $z, strlen("$parentPath/"));
$z->close();
}
}
?>
as per Usage: Methods are static so you dont need to instantiate an option like in the first example, just call the function direct using heirarchical operators
HZip::zipDir('/path/to/sourceDir', '/path/to/out.zip');
Two points to note
1. User -r in your exec , if you want to include the subdirectories as well.
2. replace * with . in your exec command and it will zip.
Related
I'm trying to create zip file from a large folder which size almost 2GB. My code is working well on the localhost but it's not working on the server(Cpanel). In server, it's creating a zip file which size is only 103 MB out of 2GB. According to my strategy, first of all, I'm creating a backup folder recursively named "system_backup". And the backup folder is creating well without any problem. The next is, to create the zip file of 'system_backup' folder by calling the function ZipData and stored it to another folder. In this time, it's not creating the zip file properly.
After that, the function rrmdir will be called. And it will delete the 'system_backup' folder recursively. And the deletion is not working properly as well. And, in localhost, it works well.
Then, when I'm trying to download the created zip file by the function download_file, it also not download properly. It's downloaded as a broken zip file. And, in localhost, it also works well.
I have already checked the read and write permission of folders and files.
The code is given below:-
public function backup_app(){
//Backup System
ini_set('memory_limit', '-1');
set_time_limit(0);
$this->recurse_copy(FCPATH,'system_backup');
$backup_name = 'Customs-system-backup-on_'. date("Y-m-d-H-i-s") .'.zip';
$path = FCPATH.'system_backup';
$destination = FCPATH.'bdCustomsBackup/'.$backup_name;
$this->zipData($path, $destination);
//Delete directory
$this->rrmdir($path);
$message = "Application Backup on ".date("Y-m-d-H-i-s");
$this->submit_log($message);
echo 1;
}
function zipData($source, $destination) {
if (extension_loaded('zip')) {
if (file_exists($source)) {
$zip = new ZipArchive();
if ($zip->open($destination, ZIPARCHIVE::CREATE)) {
$source = realpath($source);
if (is_dir($source)) {
$iterator = new RecursiveDirectoryIterator($source);
// skip dot files while iterating
$iterator->setFlags(RecursiveDirectoryIterator::SKIP_DOTS);
$files = new RecursiveIteratorIterator($iterator, RecursiveIteratorIterator::SELF_FIRST);
$counter = 1;
foreach ($files as $file) {
$file = realpath($file);
if (is_dir($file)) {
$zip->addEmptyDir(str_replace($source . '/', 'system_backup/', $file . '/'));
} else if (is_file($file)) {
$zip->addFromString(str_replace($source . '/', 'system_backup/', $file), file_get_contents($file));
}
}
} else if (is_file($source)) {
$zip->addFromString(basename($source), file_get_contents($source));
}
}
return $zip->close();
}
}
return false;
}
public function recurse_copy($src,$dst) {
$dir = opendir($src);
#mkdir($dst);
while(false !== ( $file = readdir($dir)) ) {
if (( $file != '.' ) && ( $file != '..' ) && ( $file != $dst ) && ( $file != "bdCustomsBackup" )) {
if ( is_dir($src . '/' . $file) ) {
$this->recurse_copy($src . '/' . $file, $dst . '/' . $file);
}
else {
copy($src . '/' . $file,$dst . '/' . $file);
}
}
}
closedir($dir);
}
public function rrmdir($src) {
$dir = opendir($src);
while(false !== ( $file = readdir($dir)) ) {
if (( $file != '.' ) && ( $file != '..' )) {
$full = $src . '/' . $file;
if ( is_dir($full) ) {
$this->rrmdir($full);
}
else {
unlink($full);
}
}
}
closedir($dir);
rmdir($src);
}
public function download_file($file){
$message = "Download ".$file." on ".date("Y-m-d-H-i-s");
$this->submit_log($message);
$path = FCPATH.'bdCustomsBackup/'.$file;
$this->load->helper('download_helper');
force_download($file, $path);
}
Here is the custom download_helper:-
<?php if ( ! defined('BASEPATH')) exit('No direct script access allowed');
if ( ! function_exists('force_download'))
{
function force_download($filename = '', $file = '')
{
if ($filename == '' OR $file == '')
{
return FALSE;
}
// Try to determine if the filename includes a file extension.
// We need it in order to set the MIME type
if (FALSE === strpos($filename, '.'))
{
return FALSE;
}
// Grab the file extension
$x = explode('.', $filename);
$extension = end($x);
// Load the mime types
#include(APPPATH.'config/mimes'.EXT);
// Set a default mime if we can't find it
if ( ! isset($mimes[$extension]))
{
$mime = 'application/octet-stream';
}
else
{
$mime = (is_array($mimes[$extension])) ? $mimes[$extension][0] : $mimes[$extension];
}
// Generate the server headers
if (strpos($_SERVER['HTTP_USER_AGENT'], "MSIE") !== FALSE)
{
header('Content-Type: "'.$mime.'"');
header('Content-Disposition: attachment; filename="'.$filename.'"');
header('Expires: 0');
header('Cache-Control: must-revalidate, post-check=0, pre-check=0');
header("Content-Transfer-Encoding: binary");
header('Pragma: public');
header("Content-Length: ".filesize($file));
}
else
{
header('Content-Type: "'.$mime.'"');
header('Content-Disposition: attachment; filename="'.$filename.'"');
header("Content-Transfer-Encoding: binary");
header('Expires: 0');
header('Pragma: no-cache');
header("Content-Length: ".filesize($file));
}
readfile_chunked($file);
die;
}
}
if ( ! function_exists('readfile_chunked'))
{
function readfile_chunked($file, $retbytes=TRUE)
{
$chunksize = 1 * (1024 * 1024);
$buffer = '';
$cnt =0;
$handle = fopen($file, 'r');
if ($handle === FALSE)
{
return FALSE;
}
while (!feof($handle))
{
$buffer = fread($handle, $chunksize);
echo $buffer;
ob_flush();
flush();
if ($retbytes)
{
$cnt += strlen($buffer);
}
}
$status = fclose($handle);
if ($retbytes AND $status)
{
return $cnt;
}
return $status;
}
}
/* End of file download_helper.php */
/* Location: ./application/helpers/download_helper.php */
The below code is using PHP:
$zip = new ZipArchive;
if ($zip->open('test_new.zip', ZipArchive::CREATE) === TRUE)
{
// Add files to the zip file
$zip->addFile('test.txt');
$zip->addFile('test.pdf');
// Add random.txt file to zip and rename it to newfile.txt
$zip->addFile('random.txt', 'newfile.txt');
// Add a file new.txt file to zip using the text specified
$zip->addFromString('new.txt', 'text to be added to the new.txt file');
// All files are added, so close the zip file.
$zip->close();
}
Explanation of code
Line 1 creates an object of the ZipArchive class
Line 2 opens a file with filename as test_new.zip so that we can add files to it. The flag ZipArchive::CREATE specifies that we want to create a new zip file
Lines 5 & 6 are used to add files to the zip file
Line 9 is used to add a file with name random.txt to the zip file and rename it in the zipfile as newfile.txt
Line 12 is used to add a new file new.txt with contents of the file as ‘text to be added to the new.txt file’
Line 15 closes and saves the changes to the zip file
Note: Sometimes there can be issues when using relative paths for files. If there are any issues using paths then we can also use absolute paths for files
Overwrite an existing zip file
If you want to overwrite an existing zip file then we can use code similar to following. The flag ZipArchive::OVERWRITE overwrites the existing zip file.
$zip = new ZipArchive;
if ($zip->open('test_overwrite.zip', ZipArchive::OVERWRITE) === TRUE)
{
// Add file to the zip file
$zip->addFile('test.txt');
$zip->addFile('test.pdf');
// All files are added, so close the zip file.
$zip->close();
}
Explanation of code
This code will create a file test_overwrite.zip if it already exists the file will be overwritten with this new file
Create a new zip file and add files to be inside a folder
$zip = new ZipArchive;
if ($zip->open('test_folder.zip', ZipArchive::CREATE) === TRUE)
{
// Add files to the zip file inside demo_folder
$zip->addFile('text.txt', 'demo_folder/test.txt');
$zip->addFile('test.pdf', 'demo_folder/test.pdf');
// Add random.txt file to zip and rename it to newfile.txt and store in demo_folder
$zip->addFile('random.txt', 'demo_folder/newfile.txt');
// Add a file demo_folder/new.txt file to zip using the text specified
$zip->addFromString('demo_folder/new.txt', 'text to be added to the new.txt file');
// All files are added, so close the zip file.
$zip->close();
}
Explanation of code
The above code will add different files inside the zip file to be inside a folder demo_folder
The 2nd parameter to addfile function can be used to store the file in a new folder
The 1st parameter in the addFromString function can be used to store the file in a new folder
Create a new zip file and move the files to be in different folders
$zip = new ZipArchive;
if ($zip->open('test_folder_change.zip', ZipArchive::CREATE) === TRUE)
{
// Add files to the zip file
$zip->addFile('text.txt', 'demo_folder/test.txt');
$zip->addFile('test.pdf', 'demo_folder1/test.pdf');
// All files are added, so close the zip file.
$zip->close();
}
Explanation of code
We store the file test.txt into demo_folder and test.pdf into demo_folder1
Create a zip file with all files from a directory
$zip = new ZipArchive;
if ($zip->open('test_dir.zip', ZipArchive::OVERWRITE) === TRUE)
{
if ($handle = opendir('demo_folder'))
{
// Add all files inside the directory
while (false !== ($entry = readdir($handle)))
{
if ($entry != "." && $entry != ".." && !is_dir('demo_folder/' . $entry))
{
$zip->addFile('demo_folder/' . $entry);
}
}
closedir($handle);
}
$zip->close();
}
Explanation of code
Lines 5-16 opens a directory and creates a zip file with all files within that directory
Line 5 opens the directory
Line 7 gets the name of each file in the dir
Line 9 skips the “.” and “..” and any other directories
Line 11 adds the file into the zip file
Line 14 closes the directory
Line 17 closes the zip file
I am having a very strange problem when zipping a directory and try to download using codeigniter.
Here is the code
$this->load->library('zip'); //Loading the zip library
$directory = $_SESSION['directory-download-path']; //Getting the directory from session
$name = basename($directory); //get the name of the folder
$name = str_replace(" ", "_", $name).".zip"; //create the zip name
unset($_SESSION['directory-download-path']); //removing it from session
$this->zip->read_dir($directory); //read the directory
$this->zip->download($name); //download the zip
It is very simple. The problem occurs with the download. I get the zip file but when i extract it i get a file with .zip.cpgz and continues to extract similar files. Thus i think it is corrupted. Can you please help me why is this occurring. I have permissions and everything on the directory because i am doing other operations.
EDIT:
I found after some more research to add the second parameter as follows:
$this->zip->read_dir($directory, false); //read the directory
But still not working.
Another solution was to add
ob_end_clean();
just before the line: $this->zip->download($name); //download the zip
but still no success!
Since I had no response i made a function outside of codeigniter that zips files and folder recursively which is below:
public 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);
if( in_array(substr($file, strrpos($file, '/')+1), array('.', '..', "Thumbs.db")) )
continue;
$file = 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();
}
After this in the codeigniter controller i made this:
$this->projects->Zip($source, $destination);
header("Content-type: application/zip");
header("Content-Disposition: attachment; filename=$name");
header("Content-length: " . filesize($destination));
header("Pragma: no-cache");
header("Expires: 0");
readfile($destination);
unlink($destination);
Of course the $destination should be to a temp folder where you have permissions 777 or equivalent so that when the file is sent to the client it will be deleted.
Hope this helps!
I am able to Zip and download the folder from my local machine using the following code. But I want to download a folder from my web server. How can i do it. please help. I searched a lot on google but i couldn't find a solution.
$the_folder = 'C:/Program Files/Red5/webapps/SOSample/streams/';
$zip_file_name = 'getaaa.zip';
$download_file= true;
//$delete_file_after_download= true; doesnt work!!
class FlxZipArchive extends ZipArchive {
// $location="http://localhost/SOSample";
public function addDir($location, $name) {
$this->addEmptyDir($name);
$this->addDirDo($location, $name);
} // EO addDir;
private function addDirDo($location, $name) {
$name .= '/';
$location .= '/';
// Read all Files in Dir
$dir = opendir ($location);
while ($file = readdir($dir))
{
if ($file == '.' || $file == '..') continue;
// Rekursiv, If dir: FlxZipArchive::addDir(), else ::File();
$do = (filetype( $location . $file) == 'dir') ? 'addDir' : 'addFile';
$this->$do($location . $file, $name . $file);
}
} // EO addDirDo();
}
$za = new FlxZipArchive;
$res = $za->open($zip_file_name, ZipArchive::CREATE);
if($res === TRUE)
{
$za->addDir($the_folder, basename($the_folder));
$za->close();
}
else { echo 'Could not create a zip archive';}
if ($download_file)
{
ob_get_clean();
header("Pragma: public");
header("Expires: 0");
header("Cache-Control: must-revalidate, post-check=0, pre-check=0");
header("Cache-Control: private", false);
header("Content-Type: application/zip");
header("Content-Disposition: attachment; filename=" . basename($zip_file_name) . ";" );
header("Content-Transfer-Encoding: binary");
header("Content-Length: " . filesize($zip_file_name));
readfile($zip_file_name);
//deletes file when its done...
//if ($delete_file_after_download)
//{ unlink($zip_file_name); }
}
?>
You can't "download a folder." You have to zip it up.
Instead of giving $location="http://localhost/SOSample"; give full absolute path of your web server palce it in your web server and it will make zip file from your web server. Is your eb server windows or linux based on it give the path to $location variable.
As said you cant download a folder. However, if you have a file path, you can download files separated from each other. Using file_get_contents makes it easy. http://nl1.php.net/file_get_contents
=============== Edit: ===============
You need to recursively add files in the directory. Something like this (untested):
function createZipFromDir($dir, $zip_file) {
$zip = new ZipArchive;
if (true !== $zip->open($zip_file, ZIPARCHIVE::CREATE | ZIPARCHIVE::OVERWRITE)) {
return false;
}
zipDir($dir, $zip);
return $zip;
}
function zipDir($dir, $zip, $relative_path = DIRECTORY_SEPARATOR) {
$dir = rtrim($dir, DIRECTORY_SEPARATOR) . DIRECTORY_SEPARATOR;
if ($handle = opendir($dir)) {
while (false !== ($file = readdir($handle))) {
if (file === '.' || $file === '..') {
continue;
}
if (is_file($dir . $file)) {
$zip->addFile($dir . $file, $file);
} elseif (is_dir($dir . $file)) {
zipDir($dir . $file, $zip, $relative_path . $file);
}
}
}
closedir($handle);
}
Then call $zip = createZipFromDir('/tmp/dir', 'files.zip');
Some examples of zip, see: http://www.php.net/manual/en/zip.examples.php
(code from: How to zip a folder and download it using php?)
=============== Edit 2: ===============
Based on your comment:
opendir() is used to open a local directory and since PHP 5.0.0 on an ftp directory.
If your PHP code runs on www.domain.com then /pages/to/path is actually a local directory and you can do this:
$dir ='<wwwroot>/pages/to/path';
if ($handle = opendir($dir)) {
where wwwroot is the root of the filesystem as seen by your php code.
If you're trying to download content from another website, try e.g. file_get_contents(). Note that if the remote server lists the content of a directory the listing is in fact an HTML page generated on the fly by the server. You may find yourself needing to parse that page. A better approach is to check whether the server offers some sort of API where it sends back the content in a standardized form, e.g. in JSON format.
I need to download images from other websites to my server. Create a ZIP file with those images. automatically start download of created ZIP file. once download is complete the ZIP file and images should be deleted from my server.
Instead of automatic download, a download link is also fine. but other logic remains same.
Well, you'll have to first create the zipfile, using the ZipArchive class.
Then, send :
The right headers, indicating to the browser it should download something as a zip -- see header() -- there is an example on that manual's page that should help
The content of the zip file, using readfile()
And, finally, delete the zip file from your server, using unlink().
Note : as a security precaution, it might be wise to have a PHP script running automatically (by crontab, typically), that would delete the old zip files in your temporary directory.
This just in case your normal PHP script is, sometimes, interrupted, and doesn't delete the temporary file.
<?php
Zip('some_directory/','test.zip');
if(file_exists('test.zip')){
//Set Headers:
header('Pragma: public');
header('Expires: 0');
header('Cache-Control: must-revalidate, post-check=0, pre-check=0');
header('Last-Modified: ' . gmdate('D, d M Y H:i:s', filemtime('test.zip')) . ' GMT');
header('Content-Type: application/force-download');
header('Content-Disposition: inline; filename="test.zip"');
header('Content-Transfer-Encoding: binary');
header('Content-Length: ' . filesize('test.zip'));
header('Connection: close');
readfile('test.zip');
exit();
}
if(file_exists('test.zip')){
unlink('test.zip');
}
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();
}
?>
Any idea how many zip file downloads get interrupted and need to be continued?
If continued downloads are a small percentage of your downloads, you can delete the zip file immediately; as long as your server is still sending the file to the client, it'll remain on disk.
Once the server closes the file descriptor, the file's reference count will drop to zero, and finally its blocks on disk will be released.
But, you might spent a fair amount of time re-creating zip files if many downloads get interrupted though. Nice cheap optimization if you can get away with it.
Here's how I've been able to do it in the past. This code assumes you've written the files to a path specified by the $path variable. You might have to deal with some permissions issues on your server configuration with using php's exec
// write the files you want to zip up
file_put_contents($path . "/file", $output);
// zip up the contents
chdir($path);
exec("zip -r {$name} ./");
$filename = "{$name}.zip";
header('Content-Type: application/octet-stream');
header('Content-Disposition: attachment; filename='.urlencode($filename));
header('Content-Transfer-Encoding: binary');
readfile($filename);
Other solution: Delete past files before creation new zip file:
// Delete past zip files script
$files = glob('*.zip'); //get all file names in array
$currentTime = time(); // get current time
foreach($files as $file){ // get file from array
$lastModifiedTime = filemtime($file); // get file creation time
// get how old is file in hours:
$timeDiff = abs($currentTime - $lastModifiedTime)/(60*60);
//check if file was modified before 1 hour:
if(is_file($file) && $timeDiff > 1)
unlink($file); //delete file
}
Enable your php_curl extension; (php.ini),Then use the below code to create the zip.
create a folder class and use the code given below:
<?php
include("class/create_zip.php");
$create_zip = new create_zip();
//$url_path,$url_path2 you can use your directory path
$urls = array(
'$url_path/file1.pdf',
'$url_path2/files/files2.pdf'
); // file paths
$file_name = "vin.zip"; // zip file default name
$file_folder = rand(1,1000000000); // folder with random name
$create_zip->create_zip($urls,$file_folder,$file_name);
$create_zip->delete_directory($file_folder); //delete random folder
if(file_exists($file_name)){
$temp = file_get_contents($file_name);
unlink($file_name);
}
echo $temp;
?>
create a folder class and use the code given below:
<?php
class create_zip{
function create_zip($urls,$file_folder,$file_name){
header('Content-Type: application/octet-stream');
header('Content-Disposition: attachment; filename='.$file_name);
header('Content-Transfer-Encoding: binary');
$mkdir = mkdir($file_folder);
$zip = new ZipArchive;
$zip->open($file_name, ZipArchive::CREATE);
foreach ($urls as $url)
{
$path=pathinfo($url);
$path = $file_folder.'/'.$path['basename'];
$zip->addFile($path);
$fileopen = fopen($path, 'w');
$init = curl_init($url);
curl_setopt($init, CURLOPT_FILE, $fileopen);
$data = curl_exec($init);
curl_close($init);
fclose($fileopen);
}
$zip->close();
}
function delete_directory($dirname)
{
if (is_dir($dirname))
$dir_handle = opendir($dirname);
if (!$dir_handle)
return false;
while($file = readdir($dir_handle))
{
if ($file != "." && $file != "..")
{
if (!is_dir($dirname."/".$file))
unlink($dirname."/".$file);
else
delete_directory($dirname.'/'.$file);
}
}
closedir($dir_handle);
rmdir($dirname);
return true;
}
}
?>
I went there looking for a similar solution, and after reading the comments found this turnover : before creating your zip file in a dedicated folder (here called 'zip_files', delete all zip you estimate being older than a reasonable time (I took 24h) :
$dossier_zip='zip_files';
if(is_dir($dossier_zip))
{
$t_zip=$dossier_zip.'/*.zip'; #this allow you to let index.php, .htaccess and other stuffs...
foreach(glob($t_zip) as $old_zip)
{
if(is_file($old_zip) and filemtime($old_zip)<time()-86400)
{
unlink($old_zip);
}
}
$zipname=$dossier_zip.'/whatever_you_want_but_dedicated_to_your_user.zip';
if(is_file($zipname))
{
unlink($zipname); #to avoid mixing 2 archives
}
$zip=new ZipArchive;
#then do your zip job
By doing so, after 24h you only have the last zips created, user by user. Nothing prevents you for doing a clean by cron task sometimes, but the problem with the cron task is if someone is using the zip archive when the cron is executed it will lead to an error. Here the only possible error is if someone waits 24h to DL the archive.
Firstly, you download images from webiste
then, with the files you have downloaded you creatae zipfile (great tute)
finally you sent this zip file to browser using readfile and headers (see Example 1)
I have folder named "data". This "data" folder contains a file "filecontent.txt" and another folder named "Files". The "Files" folder contains a "info.txt" file.
So it is a folder inside folder structure.
I have to zip this folder "data"(using php) along with the file and folder inside it, and download the zipped file.
I have tried the examples available at http://www.php.net/manual/en/zip.examples.php
These examples did not work. My PHP version is 5.2.10
Please help.
I have written this code.
<?php
$zip = new ZipArchive;
if ($zip->open('check/test2.zip',ZIPARCHIVE::CREATE) === TRUE) {
if($zip->addEmptyDir('newDirectory')) {
echo 'Created a new directory';
} else {
echo 'Could not create directory';
}
$zipfilename="test2.zip";
$zipname="check/test2.zip";
header('Content-Type: application/zip');
header('Content-disposition: attachment; filename=check/test1.zip'); //header('Content-Length: ' . filesize( $zipfilename));
readfile($zipname); //$zip->close(); } else { echo failed';
}
?>
file downloaded but could not unzip
You need to recursively add files in the directory. Something like this (untested):
function createZipFromDir($dir, $zip_file) {
$zip = new ZipArchive;
if (true !== $zip->open($zip_file, ZIPARCHIVE::CREATE | ZIPARCHIVE::OVERWRITE)) {
return false;
}
zipDir($dir, $zip);
return $zip;
}
function zipDir($dir, $zip, $relative_path = DIRECTORY_SEPARATOR) {
$dir = rtrim($dir, DIRECTORY_SEPARATOR) . DIRECTORY_SEPARATOR;
if ($handle = opendir($dir)) {
while (false !== ($file = readdir($handle))) {
if (file === '.' || $file === '..') {
continue;
}
if (is_file($dir . $file)) {
$zip->addFile($dir . $file, $file);
} elseif (is_dir($dir . $file)) {
zipDir($dir . $file, $zip, $relative_path . $file);
}
}
}
closedir($handle);
}
Then call $zip = createZipFromDir('/tmp/dir', 'files.zip');
For even more win I'd recommend reading up on the SPL DirectoryIterator here
========= The only solution for me ! ! !==========
Puts all subfolders and sub-files with their structure:
<?php
$the_folder = 'path/foldername';
$zip_file_name = 'archived_name.zip';
$download_file= true;
//$delete_file_after_download= true; doesnt work!!
class FlxZipArchive extends ZipArchive {
/** Add a Dir with Files and Subdirs to the archive;;;;; #param string $location Real Location;;;; #param string $name Name in Archive;;; #author Nicolas Heimann;;;; #access private **/
public function addDir($location, $name) {
$this->addEmptyDir($name);
$this->addDirDo($location, $name);
} // EO addDir;
/** Add Files & Dirs to archive;;;; #param string $location Real Location; #param string $name Name in Archive;;;;;; #author Nicolas Heimann
* #access private **/
private function addDirDo($location, $name) {
$name .= '/';
$location .= '/';
// Read all Files in Dir
$dir = opendir ($location);
while ($file = readdir($dir))
{
if ($file == '.' || $file == '..') continue;
// Rekursiv, If dir: FlxZipArchive::addDir(), else ::File();
$do = (filetype( $location . $file) == 'dir') ? 'addDir' : 'addFile';
$this->$do($location . $file, $name . $file);
}
} // EO addDirDo();
}
$za = new FlxZipArchive;
$res = $za->open($zip_file_name, ZipArchive::CREATE);
if($res === TRUE)
{
$za->addDir($the_folder, basename($the_folder));
$za->close();
}
else { echo 'Could not create a zip archive';}
if ($download_file)
{
ob_get_clean();
header("Pragma: public");
header("Expires: 0");
header("Cache-Control: must-revalidate, post-check=0, pre-check=0");
header("Cache-Control: private", false);
header("Content-Type: application/zip");
header("Content-Disposition: attachment; filename=" . basename($zip_file_name) . ";" );
header("Content-Transfer-Encoding: binary");
header("Content-Length: " . filesize($zip_file_name));
readfile($zip_file_name);
//deletes file when its done...
//if ($delete_file_after_download)
//{ unlink($zip_file_name); }
}
?>
I had to do the same thing a few days ago and here is what I did.
1) Retrieve File/Folder structure and fill an array of items. Each item is either a file or a folder, if it's a folder, retrieve its content as items the same way.
2) Parse that array and generate the zip file.
Put my code below, you will of course have to adapt it depending on how your application was made.
// Get files
$items['items'] = $this->getFilesStructureinFolder($folderId);
$archiveName = $baseDir . 'temp_' . time(). '.zip';
if (!extension_loaded('zip')) {
dl('zip.so');
}
//all files added now
$zip = new ZipArchive();
$zip->open($archiveName, ZipArchive::OVERWRITE);
$this->fillZipRecursive($zip, $items);
$zip->close();
//outputs file
if (!file_exists($archiveName)) {
error_log('File doesn\'t exist.');
echo 'Folder is empty';
return;
}
header("Pragma: public");
header("Expires: 0");
header("Cache-Control: must-revalidate, post-check=0, pre-check=0");
header("Cache-Control: private", false);
header("Content-Type: application/zip");
header("Content-Disposition: attachment; filename=" . basename($archiveName) . ";" );
header("Content-Transfer-Encoding: binary");
header("Content-Length: " . filesize($archiveName));
readfile($archiveName);
//deletes file when its done...
unlink($archiveName);
Methods used to fill & parse:
/**
*
* Gets all the files recursively within a folder and keeps the structure.
*
* #param int $folderId The id of the folder from which we start the search
* #return array $tree The data files/folders data structure within the given folder id
*/
public function getFilesStructureinFolder($folderId) {
$result = array();
$query = $this->db->query('SELECT * FROM xx WHERE deleted = 0 AND status = 1 AND parent_folder_id = ? ORDER BY name ASC', $folderId);
$folders = $query->result();
foreach($folders as $folder) {
$folderItem = array();
$folderItem['type'] = 'folder';
$folderItem['obj'] = $folder;
$folderItem['items'] = $this->getFilesStructureinFolder($folder->id);
$result[] = $folderItem;
}
$query = $this->db->query('SELECT * FROM xx WHERE deleted = 0 AND xx = ? AND status = 1 ORDER BY name ASC', $folderId);
$files = $query->result();
foreach ($files as $file) {
$fileItem = array();
$fileItem['type'] = 'file';
$fileItem['obj'] = $file;
$result[] = $fileItem;
}
return $result;
}
/**
* Fills zip file recursively
*
* #param ZipArchive $zip The zip archive we are filling
* #param Array $items The array representing the file/folder structure
* #param String $zipPath Local path within the zip
*
*/
public function fillZipRecursive($zip, $items, $zipPath = '') {
$baseDir = $this->CI->config->item('xxx');
foreach ($items['items'] as $item) {
//Item is a file
if ($item['type'] == 'file') {
$file = $item['obj'];
$fileName = $baseDir . '/' . $file->fs_folder_id . '/' . $file->file_name;
if (trim($file->file_name) == '' || !file_exists($fileName))
continue;
$zip->addFile($fileName, $zipPath.''.$file->file_name);
}
//Item is a folder
else if ($item['type'] == 'folder') {
$folder = $item['obj'];
$zip->addEmptyDir($zipPath.''.$folder->name);
//Folder probably has items in it!
if (!empty($item['items']))
$this->fillZipRecursive($zip, $item, $zipPath.'/'.$folder->name.'/');
}
}
}
See the linked duplicates. Another often overlooked and particular lazy option would be:
exec("zip -r data.zip data/");
header("Content-Type: application/zip");
readfile("data.zip"); // must be a writeable location though
Use the TbsZip class to create a new zip Archive. TbsZip is simple, it uses no temporary files, no zip EXE, it has no dependency, and has a Download feature that flushes the archive as a download file.
You just have to loop under the folder tree and add all files in the archive, and then flush it.
Code example:
$zip = new clsTbsZip(); // instantiate the class
$zip->CreateNew(); // create a virtual new zip archive
foreach (...) { // your loop to scann the folder tree
...
// add the file in the archive
$zip->FileAdd($FileInnerName, $LocalFilePath, TBSZIP_FILE);
}
// flush the result as an HTTP download
$zip->Flush(TBSZIP_DOWNLOAD, 'my_archive.zip');
Files added in the archive will be compressed sequentially during the Flush() method. So your archive can contain numerous sub-files, this won't increase the PHP memory.