Reputation: 3483
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
Upvotes: 4
Views: 25312
Reputation: 5552
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.
Upvotes: 1
Reputation: 56341
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); }
}
?>
Upvotes: 5
Reputation: 2552
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
Upvotes: 5
Reputation: 689
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.'/');
}
}
}
Upvotes: 2
Reputation: 145472
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
Upvotes: 1