5

I'm creating my first web application using php hosted on Amazon Elastic Beanstalk, and I'm a little in over my head with what to do. My task is to reach out to files specified by an end customer in an AWS S3 cloud, zip them up, and finally provide a download link to the resulting zip file. I've done a lot of hunting around to find an instance of what exactly I'm trying to do, but my inexperience with php has been a hinderince in determining if a certain solution would work for me.

I found this question and response here, and seeing that it seems to address php and zip downloads in a general sense, I thought I might be able to adapt it to my needs. Below is what I have in php:

<?php
error_reporting(E_ALL);
ini_set('display_errors', 1);

require "./aws.phar";
use Aws\S3\S3Client;

$client = S3Client::factory(array(
    'key'    => getenv("AWS_ACCESS_KEY_ID"),
    'secret' => getenv("AWS_SECRET_KEY")
));

echo "Starting zip test";

$client->registerStreamWrapper();

// make sure to send all headers first
// Content-Type is the most important one (probably)
//
header('Content-Type: application/octet-stream');
header('Content-disposition: attachment; filename="file.zip"');

// use popen to execute a unix command pipeline
// and grab the stdout as a php stream
// (you can use proc_open instead if you need to 
// control the input of the pipeline too)
//
$fp = popen('zip -r - s3://myBucket/test.txt s3://myBucket/img.png', 'r');

// pick a bufsize that makes you happy (8192 has been suggested).
$bufsize = 8192;
$buff = '';
while( !feof($fp) ) {
   $buff = fread($fp, $bufsize);
   echo $buff;
}
pclose($fp);

And here is what I'm using to call it:

$(document).ready(function() {
    $("#download_button").click(function() {
        $.get("../php/ZipAndDownload.php", function(data){alert(data)});
        return false;
    });
});

I've also tried:

$(document).ready(function() {
        $("#download_button").click(function() {
            $.ajax({
                url:url,
                type:"GET",
                complete: function (response) {
                    $('#output').html(response.responseText);
                },
                error: function () {
                    $('#output').html('Bummer: there was an error!');
                }
            });
            return false;
        });
    });

Now whenever I click the download button, I get an echo of "Starting zip test" and nothing else. No errors, and no zip file. What do I need to know or what am I doing obviously wrong?

Thank you in advance for your help and advice.

EDIT: Here's what I have after some advice from Derek. This still produces a big nasty string of binary.

<?php
header('Content-Type: application/octet-stream');
header('Content-disposition: attachment; filename="file.zip"');

require "./aws.phar";
use Aws\S3\S3Client;

$bucket = 'myBucket';

$client = S3Client::factory(array(
    'key'    => getenv('AWS_ACCESS_KEY_ID'),
    'secret' => getenv('AWS_SECRET_KEY')
));

$result = $client->getObject(array(
    'Bucket' => $bucket,
    'Key'    => 'test.txt',
    'SaveAs' => '/tmp/test.txt'
));

$Uri = $result['Body']->getUri();

$fp = popen('zip -r - '.$Uri, 'r');

$bufsize = 8192;
$buff = '';
while( !feof($fp) ) {
   $buff = fread($fp, $bufsize);
   echo $buff;
}
pclose($fp);
?>
Community
  • 1
  • 1
KDhyne
  • 126
  • 1
  • 1
  • 14
  • 2
    you're using popen, which means you're executing an external program. Command line apps will have absolutely **NO** idea what the heck `s3://etc...` means. They don't deal with URLs. they deal with filesystem paths. At best you could use an NFS or UNC path, if you're on unix/windows. – Marc B Oct 06 '14 at 20:42

1 Answers1

4

Marc B's comment is correct. The call to zip does not know what s3:// means.

You will need to download the file from S3, then zip it locally. You have several options for downloading the file from S3. For example, you could use:

$client->getObjectUrl($bucket, $key) 

to get a URL for your object, then use cUrl or wget to download from that URL, depending on your permissions settings.

Once you have the file locally, update your zip command using the location where you saved the file in order to generate the .zip file.

Derek Kurth
  • 1,633
  • 1
  • 17
  • 19
  • Thank you for your tips Derek. I've changed my script to use S3's getObject() with the option to save the file in local storage. Referencing the location of the file in the local directory seems to create a zip package as I'd hoped, but when I echo the result I get a giant string of binary representing my zip file rather than a prompt to download it. I've tried changing the Content-type header to application/zip, but to no avail. What else could I be missing? – KDhyne Oct 07 '14 at 17:10
  • Are you echoing anything before your header('Content-type: ...') line? Or, if not, does your page have any characters (even a newline) before the – Derek Kurth Oct 07 '14 at 17:50
  • Still no dice. I've cut my script down to the necessities and tried your suggestions. I've edited my original post with what I currently have. The only thing I can think of is that perhaps the jquery I'm using to call the php script is to blame? If there's nothing you can see wrong with it though, I may have to look for another approach. Thank you for your help. – KDhyne Oct 07 '14 at 20:51
  • I think the problem is the line: echo "Starting zip test"; You are sending a response to the browser BEFORE the content headers, so the headers are being ignored. – Derek Kurth Oct 07 '14 at 20:55
  • I've added an edit below the original post. I took out that echo statement after your first comment. Sorry for the confusion. – KDhyne Oct 07 '14 at 20:57
  • Ah, sorry, I see the edit now. The error doesn't jump out at me. – Derek Kurth Oct 07 '14 at 21:02
  • Here's a related post very similar to yours. Apart from the ob_flush, I don't see any big differences, but you might notice something helpful: http://stackoverflow.com/questions/6914912/streaming-a-large-file-using-php – Derek Kurth Oct 07 '14 at 21:04
  • This is my last gambit before I give up and move on to reworking my project's architecture. I've tried removing anything to do with AWS and running both the example I linked as well as yours, and both produce the same result: a string of binary representing the zip file. This is leading me to believe there may be something wrong with what I'm using to call the php script in the first place. I've updated my post with an additional ajax request I've been testing with (I don't fully understand ajax either), but it has produced the same output. Any final pieces of advice? Please and Thank you! – KDhyne Oct 08 '14 at 17:49
  • I suspect you're right. I copied your PHP code and got it running locally, and the file actually downloads! So maybe it's something wrong with your Apache (or whatever webserver you're running) config. – Derek Kurth Oct 08 '14 at 18:13
  • Well since it seems the issue I originally asked about is resolved, I will now mark this correct. I guess I get to have fun tinkering with Elastic Beanstalk some more to figure out what's causing the hangup. Thank you for all your help. – KDhyne Oct 08 '14 at 18:36