Extract a file from a ZIP string

After some hours of research I think it's surprisingly not possible do handle a zip without a temporary file:

  1. The first try with php://memory will not work, beacuse it's a stream that cannot be read by functions like file_get_contents() or ZipArchive::open(). In the comments is a link to the php-bugtracker for the lack of documentation of this problem.
  2. There is a stream support ZipArchive with ::getStream() but as stated in the manual, it only supports reading operation on an opened file. So you cannot build a archive on-the-fly with that.
  3. The zip:// wrapper is also read-only: Create ZIP file with fopen() wrapper
  4. I also did some attempts with the other php wrappers/protocolls like

     file_get_contents("zip://data://text/plain;base64,{$base64_string}#test.txt")
     $zip->open("php://filter/read=convert.base64-decode/resource={$base64_string}")
     $zip->open("php://filter/read=/resource=php://memory")
    

    but for me they don't work at all, even if there are examples like that in the manual. So you have to swallow the pill and create a temporary file.


Original Answer:

This is just the way of temporary storing. I hope you manage the zip handling and parsing of xml on your own.

Use the php php://memory (doc) wrapper. Be aware, that this is only usefull for small files, because its stored in the memory - obviously. Otherwise use php://temp instead.

<?php

// the decoded content of your zip file
$text = 'base64 _decoded_ zip content';

// this will empty the memory and appen your zip content
$written = file_put_contents('php://memory', $text);

// bytes written to memory
var_dump($written);

// new instance of the ZipArchive
$zip = new ZipArchive;

// success of the archive reading
var_dump(true === $zip->open('php://memory'));

toster-cx had it right,you should award him the points, this is an example where the zip comes from a soap response as a byte array (binary), the content is an XML file:

$objResponse = $objClient->__soapCall("sendBill",array(parameters));
$fileData=unzipByteArray($objResponse->applicationResponse);
header("Content-type: text/xml");
echo $fileData;
function unzipByteArray($data){
  /*this firts is a directory*/
  $head = unpack("Vsig/vver/vflag/vmeth/vmodt/vmodd/Vcrc/Vcsize/Vsize/vnamelen/vexlen", substr($data,0,30));
  $filename = substr($data,30,$head['namelen']);
  $if=30+$head['namelen']+$head['exlen']+$head['csize'];
 /*this second is the actua file*/
  $head = unpack("Vsig/vver/vflag/vmeth/vmodt/vmodd/Vcrc/Vcsize/Vsize/vnamelen/vexlen", substr($data,$if,30));
  $raw = gzinflate(substr($data,$if+$head['namelen']+$head['exlen']+30,$head['csize']));
  /*you can create a loop and continue decompressing more files if the were*/
  return $raw;
}

I had a similar problem, I ended up doing it manually.
https://www.pkware.com/documents/casestudies/APPNOTE.TXT

This extracts a single file (just the first one), no error/crc checks, assumes deflate was used.

// zip in a string
$data = file_get_contents('test.zip');

// magic
$head = unpack("Vsig/vver/vflag/vmeth/vmodt/vmodd/Vcrc/Vcsize/Vsize/vnamelen/vexlen", substr($data,0,30));
$filename = substr($data,30,$head['namelen']);
$raw = gzinflate(substr($data,30+$head['namelen']+$head['exlen'],$head['csize']));

// first file uncompressed and ready to use
file_put_contents($filename,$raw);

Tags:

Php