11

I would like to download a zip archive and unzip it in memory using PHP.

This is what I have today (and it's just too much file-handling for me :) ):

// download the data file from the real page
copy("http://www.curriculummagic.com/AdvancedBalloons.kmz", "./data/zip.kmz");

// unzip it
$zip = new ZipArchive;
$res = $zip->open('./data/zip.kmz');
if ($res === TRUE) {
    $zip->extractTo('./data');
    $zip->close();
}

// use the unzipped files...
dacwe
  • 43,066
  • 12
  • 116
  • 140

5 Answers5

18

Warning: This cannot be done in memory — ZipArchive cannot work with "memory mapped files".

You can obtain the data of a file inside a zip-file into a variable (memory) with file_get_contentsDocs as it supports the zip:// Stream wrapper Docs:

$zipFile = './data/zip.kmz';     # path of zip-file
$fileInZip = 'test.txt';         # name the file to obtain

# read the file's data:
$path = sprintf('zip://%s#%s', $zipFile, $fileInZip);
$fileData = file_get_contents($path);

You can only access local files with zip:// or via ZipArchive. For that you can first copy the contents to a temporary file and work with it:

$zip = 'http://www.curriculummagic.com/AdvancedBalloons.kmz';
$file = 'doc.kml';

$ext = pathinfo($zip, PATHINFO_EXTENSION);
$temp = tempnam(sys_get_temp_dir(), $ext);
copy($zip, $temp);
$data = file_get_contents("zip://$temp#$file");
unlink($temp);
Gilles 'SO- stop being evil'
  • 104,111
  • 38
  • 209
  • 254
hakre
  • 193,403
  • 52
  • 435
  • 836
  • No, `zip://` supports only local files so far (as well as ZipArchive). You have to have it accessible via your standard file-system. Will update the answer to make this visible for your `http` url. – hakre Sep 12 '11 at 19:09
  • No, stacked streams are not supported, I guess even `file://` won't work. It needs to be the path to the filesystem. Same for the `ZipArchive` class, I assume this relates to each other. Streams are not that well supported within the zip PHP extension, which is a bit pity in my eyes. Go for the tempfile to work around that, the second code example. You can encapsulate that into a class of it's own extending/decorating ZipArchive and provide a file-system wrapper that is capable of to have this more fluent. – hakre Sep 15 '11 at 10:54
  • @dacwe: If your file-system mounts a ramdisk, there is no problem at all to have `ZipArchive` work in memory - for what it matters :) – hakre Sep 15 '11 at 12:21
  • Yeah, that was actually the way I solved it, but it's not a php solution.. :) – dacwe Sep 15 '11 at 13:11
5

Old subject but still relevant since I asked myself the same question, without finding an answer.

I ended up writing this function which returns an array containing the name of each file contained in the archive, as well as the decompressed contents of that file:

function GetZipContent(String $body_containing_zip_file) {

    $sectors = explode("\x50\x4b\x01\x02", $body_containing_zip_file);
    array_pop($sectors);
    $files = explode("\x50\x4b\x03\x04", implode("\x50\x4b\x01\x02", $sectors));
    array_shift($files);

    $result = array();
    foreach($files as $file) {
        $header = unpack("vversion/vflag/vmethod/vmodification_time/vmodification_date/Vcrc/Vcompressed_size/Vuncompressed_size/vfilename_length/vextrafield_length", $file);
        array_push($result, [
            'filename' => substr($file, 26, $header['filename_length']),
            'content' => gzinflate(substr($file, 26 + $header['filename_length'], -12))
        ]);
    }
    
    return $result;
}
varun
  • 2,027
  • 1
  • 15
  • 17
Olivier JC
  • 51
  • 1
  • 1
4

As easy as:

$zipFile = "test.zip";
$fileInsideZip = "somefile.txt";
$content = file_get_contents("zip://$zipFile#$fileInsideZip");
Pedro Lobito
  • 94,083
  • 31
  • 258
  • 268
1

You can get a stream to a file inside the zip and extract it into a variable:

$fp = $zip->getStream('test.txt');
if(!$fp) exit("failed\n");

while (!feof($fp)) {
    $contents .= fread($fp, 1024);
}

fclose($fp);
CodeCaster
  • 147,647
  • 23
  • 218
  • 272
-1

If you can use system calls, the simplest way should look like this (bzip2 case). You just use stdout.

$out=shell_exec('bzip2 -dkc '.$zip);
user965748
  • 2,227
  • 4
  • 22
  • 30