0

I'm working on a system in laravel to make a zip full of photos and download it afterwards.

I decided i really don't want to use libraries for this(it is necessary), so I must use plain php. my controller code:

public function downloadPictures()
{
    $pictures = Input::get('photos');
    $file_paths = array();

    foreach ($pictures as $picture_id) {
         $path = $this->photos->getFilepath($picture_id, 'original');
         array_push($file_paths, $path);
    }

    $zip = new ZipArchive;
    $zip->open('slike.zip', ZipArchive::CREATE);

    foreach ($file_paths as $file) {
        $content = file_get_contents($file);
        $zip->addFromString(basename($file), $content);
    }

    header("Pragma: public");
    header("Expires: 0");
    header("Cache-Control: must-revalidate, post-check=0, pre-check=0");
    header("Cache-Control: private",false);
    header('Content-type: application/zip');
    header('Content-Disposition: attachment; filename="'.basename($zip->filename).'"');

    echo var_dump($zip);
    echo basename($zip->filename);

    $zip->close();

    echo var_dump($zip);
    echo basename($zip->filename);
}

Now with the first part of the code i can say with 100% confidence that i get the right picture paths(that is above the //if (file_exists($file_paths ... ) comment, and i made sure by printing them out and using file_exists)

But then things start getting wonky.

Firstly: when i open the zip the numFiles i tested says there are allready 4 files in it, i have no idea why.

Secondly: when i print the response from this controller function in js frontend(using echo in controller var_dump($zip)) i get the zip file properties.. name+ numFile+4 (+4 for some reason), but when i $zip->close() i can't access the properites echoed zip properties are empty.

Third: the redirect headers, which i used in both ways: before i closed the $zip and after(currently they are before the closing) are not doing anything.. they should produce a donwload form in a browser, are they not?

If somebody could help me i would be so greatful. I need to do this due sunday and i have been fiddling around this for about 8hrs now.(this is my first time doing this). I have done quite a lot of googling and it works for others. I am on ununtu on nginx, php v5.6 and i installed the php zip extension and i am testing it localy on ubuntu mozilla browser.

UPDATE: It doesn't work in chrome either, so it's not firefoxes problem.

EdwardMaiden
  • 3
  • 1
  • 8

1 Answers1

1

Try this:

public function downloadPictures()
{
    $pictures = Input::get('photos');
    $file_paths = array();

    foreach ($pictures as $picture_id) {
        $path = $this->photos->getFilepath($picture_id, 'original');
        array_push($file_paths, $path);
    }

    $zip = new ZipArchive;
    $zipname = 'silke.zip';
    $zip->open($zipname, ZipArchive::CREATE);

    foreach ($file_paths as $file) {
        $content = file_get_contents($file);
        $zip->addFromString(basename($file), $content);
    }

    header("HTTP/1.1 200 OK");
    header("Pragma: public");
    header("Expires: 0");
    header("Cache-Control: must-revalidate, post-check=0, pre-check=0");
    header("Cache-Control: private",false);
    header('Content-type: application/zip');
    header('Content-Disposition: attachment; filename="'.$zipname.'"');
    header('Content-Length: ' . filesize($zipname));

    $zip->close();
    readfile($zipname);
}

Most important is the Content-Lengthand the readfileline.

Don't echo anything else, it will corrupt the download.

Update: A more selfcontained test:

$testData = array(
    'test1.txt' => 'Test1',
    'test2.txt' => 'Test2',
    'test3.txt' => 'Test3',
    'test4.txt' => 'Test4',
);

$zip = new ZipArchive;
$zipname = 'slike.zip';
$zip->open($zipname, ZipArchive::CREATE);

foreach ($testData as $filename => $content) {
    $zip->addFromString($filename, $content);
}

header("HTTP/1.1 200 OK");
header("Pragma: public");
header("Expires: 0");
header("Cache-Control: must-revalidate, post-check=0, pre-check=0");
header("Cache-Control: private",false);
header('Content-Type: application/force-download');
header('Content-Disposition: attachment; filename="'.$zipname.'"');
header('Content-Length: ' . filesize($zipname));

$zip->close();
readfile($zipname);

Also this has header('Content-Type: application/force-download');

Torge
  • 2,174
  • 1
  • 23
  • 33
  • Thanks for the input, i guess the $zip is all blank beacuse it is closed, but the file remains(maybe?) allthough the solution does not seem to work, strange thing .. the readfile() produces this https://postimg.org/image/k4rzlo2ud/ popup, maybe it has to do something with it not working? – EdwardMaiden Aug 12 '16 at 12:26
  • Could it be possible that something is blocking download/ browser response/ download prompts/ on ubuntu/mozilla/server something to do with the server config? browser config/ubuntu config? – EdwardMaiden Aug 12 '16 at 12:33
  • Try to run the new selfcontained test, that I added, as a single php file directly. This was what worked fine for me in my environment. – Torge Aug 12 '16 at 12:51
  • I'm guessing you are using a standalone php environment or what? Perhaps my problem is that i have this in a laravel php environment? – EdwardMaiden Aug 12 '16 at 13:06
  • Can you try this in an isolated php script without laravel, to see if it works in general? Maybe laravel sends some more data down the line by default and you have to prevent it somehow? – Torge Aug 12 '16 at 13:47
  • Sorry for delay, had to be somewhere. Yes. It works in a isolated environment. Please. Could you help me figure out what to do or at least point me in the right way? I'm really frustrated. Lost over 6 hours on this blocker even though it might look stupid that i didn't think of that. – EdwardMaiden Aug 12 '16 at 18:20
  • I expect that your laravel environment or your other code is making the data channel dirty. It might echo something by default before or after. Or have some kind of buffering enabled. One thing you can try is to put an `die();` after `readfile` to make sure that nothing else echos something After the zip file. Other thing you can try is to comment the `readfile`, make the call and see if you get anything from the server. This should NOT be the case. If, you have the reason for the problem. – Torge Aug 12 '16 at 23:53
  • Long story short, it doesn't seem to work. I looked up laravel more and found out, that it actually has its own Response::download facade, but when i used that same thing happened again() - postimg.org/image/k4rzlo2ud and no file prompt. The firefox response header says there is a attachment zip file, it returns a successful response header. – EdwardMaiden Aug 16 '16 at 06:44