S3 Streaming With PHP

Steven pointed out that someone was looking for a way to stream to S3 using PHP and said I should figure out how to get it going. Since he made a patch to let you stream data with Ruby using S3 I figured I should do one for PHP. There may be better ways of doing this but since I’ve already done it with C using curl I figured that would be the fastest way.

First you will want to get the S3 PHP library from Amazon. Then you add the following to the file:

/**
 * putObjectStream -- Streams data to a bucket.
 *
 * Takes ($bucket, $key, $streamFunction, $contentType, $contentLength [,$acl, $metadataArray, $md5])
 *
 *
 * - [str] $bucket: the bucket into which file will be written
 * - [str] $key: key of written file
 * - [str] $streamFunction: function to call for data to stream
 * - [str] $contentType: file content type
 * - [str] $contentLength: file content length
 * - [str] $acl: access control policy of file (OPTIONAL: defaults to 'private')
 * - [str] $metadataArray: associative array containing user-defined metadata (name=>value) (OPTIONAL)
 * - [bool] $md5: the MD5 hash of the object (OPTIONAL)
*/
function putObjectStream($bucket, $key, $streamFunction, $contentType, $contentLength, $acl, $metadataArray, $md5){
        sort($metadataArray);
        $resource = "$bucket/$key";
        $resource = urlencode($resource);
        $httpDate = gmdate("D, d M Y G:i:s T");

        $curl_inst = curl_init();

        curl_setopt ($curl_inst, CURLOPT_CONNECTTIMEOUT, 30);
        curl_setopt ($curl_inst, CURLOPT_LOW_SPEED_LIMIT, 1);
        curl_setopt ($curl_inst, CURLOPT_LOW_SPEED_TIME, 180);
        curl_setopt ($curl_inst, CURLOPT_NOSIGNAL, 1);
        curl_setopt ($curl_inst, CURLOPT_READFUNCTION, $streamFunction);
        curl_setopt ($curl_inst, CURLOPT_URL, $this->serviceUrl . $resource);
        curl_setopt ($curl_inst, CURLOPT_UPLOAD, true);
        curl_setopt ($curl_inst, CURLINFO_CONTENT_LENGTH_UPLOAD, $contentLength);

        $header[] = "Date: $httpDate";
        $header[] = "Content-Type: $contentType";
        $header[] = "Content-Length: $contentLength";
        $header[] = "Expect: ";
        $header[] = "Transfer-Encoding: ";
        $header[] = "x-amz-acl: $acl";

        $MD5 = "";
        if($md5){
                $MD5 = $this->hex2b64(md5_file($filePath));
                $header[] = "Content-MD5: $MD5";
        }

        $stringToSign="PUT\\n$MD5\\n$contentType\\n$httpDate\\nx-amz-acl:$acl\\n";
        foreach($metadataArray as $current){
                if($current!=""){
                        $stringToSign.="x-amz-meta-$current\\n";
                        $header = substr($current,0,strpos($current,':'));
                        $meta = substr($current,strpos($current,':')+1,strlen($current));
                        $header[] = "x-amz-meta-$header: $meta";
                }
        }

        $stringToSign.="/$resource";

        $signature = $this->constructSig($stringToSign);

        $header[] = "Authorization: AWS $this->accessKeyId:$signature";

        curl_setopt($curl_inst, CURLOPT_HTTPHEADER, $header);
        curl_setopt($curl_inst, CURLOPT_RETURNTRANSFER, 1);

        $result = curl_exec ($curl_inst);

        $this->responseString = $result;
        $this->responseCode = curl_getinfo($curl_inst, CURLINFO_HTTP_CODE);

        curl_close($curl_inst);
}

Now you have an updated s3.php you are ready to start streaming. Be aware that the content length value needs to be correct. If it is not the correct size your upload will be either truncated (too short) or it will hang (too long). If it hangs the S3 service will timeout after a small amount of time and give you an error.

Here are two examples of how to use it. First with a file:

<?php

include 's3.php';

class MyClass
{
  var $data;

  function stream_function($handle, $fd, $length)
  {
    return fread($this->data, $length);
  }
}

$my_class_inst = new MyClass();

$fsize = filesize("/tmp/largefile.tar.gz");

$my_class_inst->data = fopen("/tmp/largefile.tar.gz", "r");

$s3_inst = new s3("access key", "private key");
$s3_inst->putObjectStream("abucket", "largefile.tar.gz", array($my_class_inst, "stream_function"), "application/x-gzip", $fsize, "public-read", array(), null);

print "Response String: " . $s3_inst->responseString . "\\n";
print "Response Code: " . $s3_inst->responseCode . "\\n";
print "Parsed XML: " . $s3_inst->parsed_xml . "\\n";

fclose($my_class_inst->data);

?>

Another example of streaming some random text:

<?php

include 's3.php';

class MyClass
{
  var $data;

  function stream_function($handle, $fd, $length)
  {
    return $this->data;
  }
}

$my_class_inst = new MyClass();

$my_class_inst->data = "A test string";
$size = strlen($my_class_inst->data);

$s3_inst = new s3("access key", "private key");
$s3_inst->putObjectStream("bucketname", "test.txt", array($my_class_inst, "stream_function"), "text/html", $size, "public-read", array(), null);

print "Response String: " . $s3_inst->responseString . "\\n";
print "Response Code: " . $s3_inst->responseCode . "\\n";
print "Parsed XML: " . $s3_inst->parsed_xml . "\\n";

?>
del.icio.us:S3 Streaming With PHP digg:S3 Streaming With PHP spurl:S3 Streaming With PHP wists:S3 Streaming With PHP simpy:S3 Streaming With PHP newsvine:S3 Streaming With PHP blinklist:S3 Streaming With PHP furl:S3 Streaming With PHP reddit:S3 Streaming With PHP fark:S3 Streaming With PHP blogmarks:S3 Streaming With PHP Y!:S3 Streaming With PHP smarking:S3 Streaming With PHP magnolia:S3 Streaming With PHP segnalo:S3 Streaming With PHP gifttagging:S3 Streaming With PHP

6 Responses to “S3 Streaming With PHP”

  1. Sven Says:

    is it anyhow possible to send “fragments” of a file?

    I have this php code:
    $fp = fopen(….)

    while(!feof($fp)) {
    $fragment = fread(..);

    -> USE MISSIONDATA’S CLASS TO STREAM FILE-FRAGMENT TO S3
    }

    any possibility?

  2. carsonm Says:

    There are multiple ways you can do that. If the fragments are small enough the easiest way would probably be to just read them into a buffer then stream the buffer. If the fragment is larger you could seek to the place in the file you want to start from and then let things go from that point. One thing to keep in mind is that the S3 stream is a pump you are providing data to. From your example you are trying to be the pump to push the data.

  3. cbmeeks Says:

    Thanks for this code! I am working on a C# / PHP project and needed a way of streaming in PHP.

    Thanks

    cbmeeks
    http://www.codershangout.com

  4. Alex Ezell Says:

    What are the benefits/drawbacks of streaming a file versus the putting an object via the typical REST request?

    What are the implications of very large (multiple gig) files?

  5. mission_data Says:

    The main benefit is that for large files you don’t have them completely stored in memory.

  6. Kamal Says:

    I am using S3.php class to upload files to amazon s3. I updated the s3 class with putObjectStream function and trying to test yr example. But i am not being able to upload even 50MB file. Sometimes, i receive request time out error and sometimes, screen just goes blank and with that, i am not even being able to debug the code.

    Your help would be highly appreciated.

Leave a Reply