32

I am trying to generate an archive on-the-fly in PHP and send it to the user immediately (without saving it). I figured that there would be no need to create a file on disk as the data I'm sending isn't persistent anyway, however, upon searching the web, I couldn't find out how. I also don't care about the file format.

So, the question is:

Is it possible to create and manipulate a file archive in memory within a php script without creating a tempfile along the way?

ralokt
  • 1,855
  • 3
  • 17
  • 19
  • Can this archive contain only one file or more than one file? – VolkerK Jul 27 '09 at 17:04
  • In my specific case it contains more than one. Also I'd think that a solution for "arbitrarily" large archives (item-wise) is much more interesting (in my case, I can use a tempfile, but what about people that have to use a server where their access to the filesystem is restricted)? – ralokt Jul 28 '09 at 06:58

5 Answers5

50

I had the same problem but finally found a somewhat obscure solution and decided to share it here.

I came accross the great zip.lib.php/unzip.lib.php scripts which come with phpmyadmin and are located in the "libraries" directory.

Using zip.lib.php worked as a charm for me:

require_once(LIBS_DIR . 'zip.lib.php');

... 

//create the zip
$zip = new zipfile();

//add files to the zip, passing file contents, not actual files
$zip->addFile($file_content, $file_name);

...

//prepare the proper content type
header("Content-type: application/octet-stream");
header("Content-Disposition: attachment; filename=my_archive.zip");
header("Content-Description: Files of an applicant");

//get the zip content and send it back to the browser
echo $zip->file();

This script allows downloading of a zip, without the need of having the files as real files or saving the zip itself as a file.

It is a shame that this functionality is not part of a more generic PHP library.

Here is a link to the zip.lib.php file from the phpmyadmin source: https://github.com/phpmyadmin/phpmyadmin/blob/RELEASE_4_5_5_1/libraries/zip.lib.php

UPDATE: Make sure you remove the following check from the beginning of zip.lib.php as otherwise the script just terminates:

if (! defined('PHPMYADMIN')) {
    exit;
}

UPDATE: This code is available on the CodeIgniter project as well: https://github.com/patricksavalle/CodeIgniter/blob/439ac3a87a448ae6c2cbae0890c9f672efcae32d/system/helpers/zip_helper.php

Samuel Reid
  • 1,756
  • 12
  • 22
nettle
  • 717
  • 6
  • 22
3

what are you using to generate the archive? You might be able to use the stream php://temp or php://memory to read and write to/from the archive.

See http://php.net/manual/en/wrappers.php.php

Tom Haigh
  • 57,217
  • 21
  • 114
  • 142
  • 1
    At the moment, I'm using the php zip extension and writing a tempfile whenever I serve a download. I tried using the php://temp wrapper (figuring that it was superior to php://memory, as with large quantities of data, a tempfile IS a good idea, and hosts might allow its use even while restricting fs access. I ran into the problem that I had to close the zipfile, which appearantly freed everything in php://temp/ . (Reading from the zipfile before and after closing gave me 0 bytes.) Anyway, I don't care about the library I use, as long as it works. PHP-internal would be preferred, of course. – ralokt Jul 28 '09 at 07:10
2

Regarding your comment that php://temp works for you except when you close it, try keeping it open, flushing the output, then rewind it back to 0 and read it.

Look here for more examples: http://us.php.net/manual/en/function.tmpfile.php

Also research output buffering and capturing: http://us.php.net/manual/en/function.ob-start.php

Chloe
  • 25,162
  • 40
  • 190
  • 357
0

You need to use ZipArchive::addFromString - if you use addFile() the file is not actually added until you go to close it. (Horrible bug IMHO, what if you are trying to move files into a zip and you delete them before you close the zip...)
The addFromString() method adds it to the archive immediately.

user35443
  • 6,309
  • 12
  • 52
  • 75
-7

Is there really a performance issue here, or does it just offend your sense of rightness? A lot of processes write temporary files and delete them, and often they never hit the disk due to caching.

A tempfile is automatically deleted when closed. That's it's nature.

There are only two ways I can think of to create a zip file in memory and serve it and both are probably more trouble than they are worth.

  • use a ram disk.
  • modify the ziparchive class to add a method that does everything the close() method does, except actually close the file. (Or add a leave-open parameter to close()).
    This might not even be possible depending on the underlying C libraries.
Lucky
  • 646
  • 4
  • 9