I need to extract the contents of a directory within a zip archive in to an output directory.
The directory name inside the zip could be anything. However it will be the only directory in the base of the zip archive. There could be any number of files in the directory, in the zip archive, though.
The file structure inside the zip would be along theses lines:
- d0001
- My Folder
- view.php
- tasks.txt
- file1.txt
- picture1.png
- document.doc
The contents of the output directory needs to look like this:
- My Folder
- view.php
- tasks.txt
- file1.txt
- picture1.png
- document.doc
The code I currently have deletes the contents of the output directory and extracts the entire zip archive in to the directory:
function Unzip($source, $destination) {
$zip = new ZipArchive;
$res = $zip->open($source);
if($res === TRUE) {
$zip->extractTo($destination);
$zip->close();
return true;
} else {
return false;
}
}
function rrmdir($dir, $removebase = true) {
if(is_dir($dir)) {
$objects = scandir($dir);
foreach($objects as $object) {
if($object != "." && $object != "..") {
if(filetype($dir."/".$object) == "dir") rrmdir($dir."/".$object); else unlink($dir."/".$object);
}
}
reset($objects);
if($removebase == true)
rmdir($dir);
}
}
$filename = '/home/files.zip';
$dest = '/home/myfiles/';
if(is_dir($dest)) {
rrmdir($dest, false);
$unzip = Unzip($filename, $dest);
if($unzip === true) {
echo 'Success';
} else
echo 'Extraction of zip failed.';
} else
echo 'The output directory does not exist!';
All the function rrmdir() does is remove the contents of the output directory.
I managed to get it to work by working with file streams manually instead of using extractTo().
The script extracts all the files in the base of the archive and all the files in directories in the base of the archive in to the output folder.
For example, if this is the archives contents:
- d0001
- My Folder
- view.php
- tasks.txt
- file1.txt
- picture1.png
- document.doc
- d2
- another1.png
- pic2.gif
- doc1.txt
- mylist.txt
The contents of the output directory will look like this:
- My Folder
- view.php
- tasks.txt
- file1.txt
- picture1.png
- document.doc
- another1.png
- pic.gif
- doc1.txt
- mylist.txt
The code:
function rrmdir($dir, $removebase = true) {
if(is_dir($dir)) {
$objects = scandir($dir);
foreach($objects as $object) {
if($object != "." && $object != "..") {
if(filetype($dir."/".$object) == "dir") rrmdir($dir."/".$object); else unlink($dir."/".$object);
}
}
reset($objects);
if($removebase == true)
rmdir($dir);
}
}
$filename = '/home/files.zip';
$dest = '/home/myfiles/';
if(is_dir($dest)) {
// Remove directory's contents
rrmdir($dest, false);
// Load up the zip
$zip = new ZipArchive;
$unzip = $zip->open($filename);
if($unzip === true) {
for($i=0; $i<$zip->numFiles; $i++) {
$name = $zip->getNameIndex($i);
// Remove the first directory in the string if necessary
$parts = explode('/', $name);
if(count($parts) > 1) {
array_shift($parts);
}
$file = $dest . '/' . implode('/', $parts);
// Create the directories if necessary
$dir = dirname($file);
if(!is_dir($dir))
mkdir($dir, 0777, true);
// Check if $name is a file or directory
if(substr($file, -1) == "/") {
// $name is a directory
// Create the directory
if(!is_dir($file))
mkdir($file, 0777, true);
} else {
// $name is a file
// Read from Zip and write to disk
$fpr = $zip->getStream($name);
$fpw = fopen($file, 'w');
while($data = fread($fpr, 1024)) {
fwrite($fpw, $data);
}
fclose($fpr);
fclose($fpw);
}
}
echo 'Success';
} else
echo 'Extraction of zip failed.';
} else
echo 'The output directory does not exist!';
Related
I am creating script zipping files from an array.
/* 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;
}
}
#Mysql connect
$files_to_zip = array();
while($row = mysql_fetch_assoc($shots)) {
$files_to_zip[] = '/home/username/domains/example.com/public_html/components/items/images/item/' . $row["name"] . '_thb.' . $row["ext"];
}
if(isset($_POST['create'])){
//if true, good; if false, zip creation failed
$result = create_zip($files_to_zip,'my-archive5.zip');
}
After form submitting zip is created, but there's full directory tree inside files.
How to only zip files without directories?
Second thing:
When I open zip in total commander - I can see directories and files. When I open it normally - zip is empty.
The second argument to the addFile() method is the name of the file inside the zip. You're doing this:
$zip->addFile($file, $file);
So you're getting something like:
$zip->addFile('/path/to/some/file', '/path/to/some/file');
Which just copies the path from the source into the zip. If you want all the files to be in one dir in the zip, remove the path from the file name:
$zip->addFile($file, basename($file));
This will give you:
$zip->addFile('/path/to/some/file', 'file');
Note this won't work too well if you have files with the same name in different directories.
Check the docs on addFile method of Zip extension. You can specify local name as second argument.
Example from the documentation
<?php
$zip = new ZipArchive;
if ($zip->open('test.zip') === TRUE) {
$zip->addFile('/path/to/index.txt', 'newname.txt');
$zip->close();
echo 'ok';
} else {
echo 'failed';
}
?>
How to create Zip archive for file only particular extension in my case .TIF .JPG .TXT
currently i have added single extensions in code you can modify it later on.
<?php
/* 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;
}
}
//your directory path where all files are stored
$dir = 'D:\xampp\htdocs\samples\zip';
$files1 = scandir($dir, 1);
// $ext = "png"; //whatever extensions which you want to be in zip.
$ext = ['jpg','tif','TXT']; //whatever extensions which you want to be in zip.
$finalArray = array();
foreach ($files1 as $key => $value) {
$getExt = explode(".", $value);
if ( in_array($getExt[1] , $ext) ) {
$finalArray[$key] = $value;
}
}
$result = create_zip($finalArray, 'my-archive' . time() . '.zip');
if ($result) {
echo "Operation done.";
}
?>
i think this is what you want.
let me know if you have any issue.
I'm struggling with creating zips with ZipArchive. I've modified a function that let's me create a zip from an array of file paths.
Can you spot any faults in this code, anything that prevents the zip file from being created?
// Function to create a zip archive for the low res images
function create_zip_lres($files = array(), $destination = '', $overwrite = false) {
// If the zip file already exists and overwrite is false, return false
if(file_exists($destination) && !$overwrite) { return false; }
$valid_files = array();
// If files were passed in
if(is_array($files)) {
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) {
$basename = basename($file);
$zip->addFile($file, $basename);
}
// DEBUG
echo 'The zip archive contains ',$zip->numFiles,' files with a status of ',$zip->status;
if ($zip->close()) {
echo ' - Success!';
echo '<br>';
echo 'Your zip path is: ' . $destination;
} else {
echo ' - Failed!';
}
// DEBUG
echo '<br>';
print_r($zip);
// Return filepath for zip
return $destination;
}
else {
return false;
}
}
// getting the images array
$resize_images = resize_images();
// destination path
$lres_directory = get_lres_full_upload_dir() . get_the_title() . '-low-res.zip';
if (function_exists('create_zip_lres')) {
create_zip_lres($resize_images, $lres_directory);
} else {
}
I've added some debugging lines to find out what's happening and although it's telling me everything looks fine, no file is created.
The zip archive contains 3 files with a status of 0 - Success!
Your zip path is: http://media.mysite.com/wp-content/uploads/lres-download-manager-files/Knus 1400-low-res.zip
ZipArchive Object ( [status] => 0 [statusSys] => 0 [numFiles] => 0 [filename] => [comment] => )
I have another solutions for dynamic array values enter to zip files.I have tested this is working fine.
Hope this helps:
<?php
//Our Dynamic array
$array = array( "name" => "raaaaaa",
"test" => "/sites/chessboard.jpg"
);
//After zip files are stored in this folder
$file= "/sites/master.zip";
$zip = new ZipArchive;
echo "zip started.\n";
if ($zip->open($file, ZipArchive::CREATE) === TRUE) {
foreach($array as $path){
$zip->addFile($path, basename($path));
echo "$path was added.\n";
}
$zip->close();
echo 'done';
} else {
echo 'failed';
}
?>
zipping multiple files to one zip file.
For Multiple files you can use this code for zip the specific folders.
I have tried this code and successfully zipped multiple files into one zip file.
I think this is help to your requirement.
<?php
function Zip($source, $destination)
{
echo "called function";
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);
// Ignore "." and ".." folders
if( in_array(substr($file, strrpos($file, '/')+1), array('.', '..')) )
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();
}
Zip('D:/tmp','D:/tmp/compressed.zip',true);
?>
Hope this helps
I am allowing users to upload portfolios in ZIP archives on my site.
The problem is that most archives have the following folder structure:
zipfile.zip
- zipfile
- file1.ext
- file2.ext
- file3.ext
is there any way to simply put the files (not the directory) onto my site (so the folder structure of their portfolio is like so)
user_name
- portfolio
- file1.ext
- file2.ext
- file3.ext
it currently uploads them like so:
user_name
- portfolio
- zipfile
- file1.ext
- file2.ext
- file3.ext
which creates all kinds of problems!
I have tried doing this:
$zip = new ZipArchive();
$zip->open($_FILES['zip']['tmp_name']);
$folder = explode('.', $_FILES['zip']['name']);
end($folder);
unset($folder[key($folder)]);
$folder = (implode('.', $folder));
$zip->extractTo($root, array($folder));
$zip->close();
to no avail.
You could do something like this:
Extract Zip file to a temp location.
Scan through it and move(copy) all the files to portfolio folder.
Delete the temp folder and its all contents (created in Step 1).
Code:
//Step 01
$zip = new ZipArchive();
$zip->open($_FILES['zip']['tmp_name']);
$zip->extractTo('temp/user');
$zip->close();
//Define directories
$userdir = "user/portfolio"; // Destination
$dir = "temp/user"; //Source
//Step 02
// Get array of all files in the temp folder, recursivly
$files = dirtoarray($dir);
// Cycle through all source files to copy them in Destination
foreach ($files as $file) {
copy($dir.$file, $userdir.$file);
}
//Step 03
//Empty the dir
recursive_directory_delete($dir);
// Functions Code follows..
//to get all the recursive paths in a array
function dirtoarray($dir, $recursive) {
$array_items = array();
if ($handle = opendir($dir)) {
while (false !== ($file = readdir($handle))) {
if ($file != "." && $file != "..") {
if (is_dir($dir. "/" . $file)) {
if($recursive) {
$array_items = array_merge($array_items, dirtoarray($dir. "/" . $file, $recursive));
}
} else {
$file = $dir . "/" . $file;
$array_items[] = preg_replace("/\/\//si", "/", $file);
}
}
}
closedir($handle);
}
return $array_items;
}
// Empty the dir
function recursive_directory_delete($dir)
{
// if the path has a slash at the end we remove it here
if(substr($directory,-1) == '/')
{
$directory = substr($directory,0,-1);
}
// if the path is not valid or is not a directory ...
if(!file_exists($directory) || !is_dir($directory))
{
// ... we return false and exit the function
return FALSE;
// ... if the path is not readable
}elseif(!is_readable($directory))
{
// ... we return false and exit the function
return FALSE;
// ... else if the path is readable
}else{
// we open the directory
$handle = opendir($directory);
// and scan through the items inside
while (FALSE !== ($item = readdir($handle)))
{
// if the filepointer is not the current directory
// or the parent directory
if($item != '.' && $item != '..')
{
// we build the new path to delete
$path = $directory.'/'.$item;
// if the new path is a directory
if(is_dir($path))
{
// we call this function with the new path
recursive_directory_delete($path);
// if the new path is a file
}else{
// we remove the file
unlink($path);
}
}
}
// close the directory
closedir($handle);
// return success
return TRUE;
}
}
How if change your zip file to this?
zipfile.zip
- file1.ext
- file2.ext
- file3.ext
I am using this function to generate zip files:
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);
//echo $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
echo $destination;
return file_exists($destination);
}
else
{
return false;
}
}
I am using this to zip a vanilla, unaltered Wordpress install.(http://wordpress.org/) However for some reason the wp-content folder is having a 'ghost' empty file generated with it's name. The folder (and everything else) itself compresses correctly, but most unzip applications (including php's own extractTo()) break when they reach this unwanted file due to the name conflict.
I've looked into the folder/file structure and as far as I can see the only difference of note between this and the other folders in the site is that it is the biggest at 5.86 mb.
Can anyone suggest a fix / workaround?
This is the sample example to zip a folder.
<?php
function Create_zipArch($archive_name, $archive_folder)
{
$zip = new ZipArchive;
if ($zip->open($archive_name, ZipArchive::CREATE) === TRUE)
{
$dir = preg_replace('/[\/]{2,}/', '/', $archive_folder . "/");
$dirs = array($dir);
while (count($dirs))
{
$dir = current($dirs);
$zip->addEmptyDir($dir);
$dh = opendir($dir);
while ($file = readdir($dh))
{
if ($file != '.' && $file != '..')
{
if (is_file($file))
$zip->addFile($dir . $file, $dir . $file);
elseif (is_dir($file))
$dirs[] = $dir . $file . "/";
}
}
closedir($dh);
array_shift($dirs);
}
$zip->close();
$result='success';
}
else
{
$result='failed';
}
return $result;
}
$zipArchName = "ZipFileName.zip"; // zip file name
$FolderToZip = "zipthisfolder"; // folder to zip
echo Create_zipArch($zipArchName, $FolderToZip);
?>