Harmageddon
Harmageddon

Reputation: 61

php - extract files from folder in a zip

I have a zip file containing one folder, that contains more folders and files, like this:

myfile.zip
-firstlevel
--folder1
--folder2
--folder3
--file1
--file2

Now, I want to extract this file using PHPs ZipArchive, but without the "firstlevel" folder. At the moment, the results look like this:

destination/firstlevel/folder1
destination/firstlevel/folder2
...

The result I'd like to have would look like this:

destination/folder1
destination/folder2
...

I've tried extractTo, which produces the first mentioned result, and copy(), as suggested here, but this doesn't seem to work at all.

My current code is here:

if($zip->open('myfile.zip') === true) {
        $firstlevel = $zip->getNameIndex(0);
        for($i = 0; $i < $zip->numFiles; $i++) {
                $entry = $zip->getNameIndex($i);
                $pos = strpos($entry, $firstlevel);
                if ($pos !== false) {
                        $file = substr($entry, strlen($firstlevel));
                        if(strlen($file) > 0){
                                $files[] = $file;
                        }
                }
        }
        //attempt 1 (extractTo):
        //$zip->extractTo('./test', $files);

        //attempt 2 (copy):
        foreach($files as $filename){
                 copy('zip://'.$firstlevel.'/'.$filename, 'test/'.$filename);
        }
}

How can I achieve the result I'm aiming for?

Upvotes: 4

Views: 4031

Answers (1)

CyberPunkCodes
CyberPunkCodes

Reputation: 3777

Take a look at my Quick Unzipper script. I wrote this for personal use a while back when uploading large zip files to a server. It was a backup, and 1,000s of files take forever with FTP so using a zip file was faster. I use Git and everything, but there wasn't another option for me. I place this php file in the directory I want the files to go, and put the zip file in the same directory. For my script, they all have to operate in the same directory. It was an easy way to secure it for my needs, as everything I needed was in the same dir.

Quick Unzipper: https://github.com/incomepitbull/QuickUnzipper/blob/master/unzip.php

I linked the file because I am not showcasing the repo, just the code that makes the unzip tick. With modern versions of PHP, there should't be anything that isn't included on your setup. So you shouldn't need to do any server config changes to use this.

Here is the PHP Doc for the ZipArchive class it uses: http://php.net/manual/en/class.ziparchive.php

There isn't any included way to do what you want, which is a shame. So I would unzip the file to a temp directory, then use another function to copy the contents to where you want. So when using ZipArchive, you will need to return the first item to get the folder name if it is unknown. If the folder is known, ie: the same pesky folder name every time, then you could hard code the name.

I have made it return the first item from the index. So if you ALWAYS have a zip with 1 folder inside it, and everything in that folder, this would work. However, if you have a zip file without everything consolidated inside 1 folder, it would fail. The code I have added will take care of your question. You will need to add further logic to handle alternate cases.

Also, You will still be left with the old directory from when we extract it to the temp directory for "processing". So I included code to delete it too.

NOTE: The code uses a lot of if's to show the processing steps, and print a message for testing purposes. You would need to modify it to your needs.

<?php
public function copyDirectoryContents($source, $destination, $create=false)
{
    if ( ! is_dir($source) ) {
        return false;
    }

    if ( ! is_dir($destination) && $create === true ) {
        @mkdir($destination);
    }

    if ( is_dir($destination) ) {
        $files = array_diff(scandir($source), array('.','..'));
        foreach ($files as $file)
        {
            if ( is_dir($file) ) {
                copyDirectoryContents("$source/$file", "$destination/$file");
            } else {
                @copy("$source/$file", "$destination/$file");
            }
        }
        return true;
    }
    return false;
}

public function removeDirectory($directory, $options=array())
{
    if(!isset($options['traverseSymlinks']))
        $options['traverseSymlinks']=false;
    $files = array_diff(scandir($directory), array('.','..'));
    foreach ($files as $file)
    {
        if (is_dir("$directory/$file"))
        {
            if(!$options['traverseSymlinks'] && is_link(rtrim($file,DIRECTORY_SEPARATOR))) {
                unlink("$directory/$file");
            } else {
                removeDirectory("$directory/$file",$options);
            }
        } else {
            unlink("$directory/$file");
        }
    }
    return rmdir($directory);
}


$file = dirname(__FILE__) . '/file.zip';        // full path to zip file needing extracted
$temp = dirname(__FILE__) . '/zip-temp';        // full path to temp dir to process extractions
$path = dirname(__FILE__) . '/extracted';       // full path to final destination to put the files (not the folder)

$firstDir = null;       // holds the name of the first directory

$zip = new ZipArchive;
$res = $zip->open($file);
if ($res === TRUE) {
    $firstDir = $zip->getNameIndex(0);
    $zip->extractTo($temp);
    $zip->close();
    $status = "<strong>Success:</strong> '$file' extracted to '$temp'.";
} else {
    $status = "<strong>Error:</strong> Could not extract '$file'.";
}

echo $status . '<br />';

if ( empty($firstDir) ) {
    echo 'Error: first directory was empty!';
} else {
    $firstDir = realpath($temp . '/' . $firstDir);
    echo "First Directory: $firstDir <br />";
    if ( is_dir($firstDir) ) {
        if ( copyDirectoryContents($firstDir, $path) ) {
            echo 'Directory contents copied!<br />';
            if ( removeDirectory($directory) ) {
                echo 'Temp directory deleted!<br />';
                echo 'Done!<br />';
            } else {
                echo 'Error deleting temp directory!<br />';
            }
        } else {
            echo 'Error copying directory contents!<br />';
        }
    } else {
        echo 'Error: Could not find first directory';
    }
}

Upvotes: 2

Related Questions