<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>Mission Data Blog &#187; s3</title>
	<atom:link href="http://www.missiondata.com/blog/tag/s3/feed/" rel="self" type="application/rss+xml" />
	<link>http://www.missiondata.com/blog</link>
	<description>Louisville-based Web Development &#38; Software Engineering</description>
	<lastBuildDate>Tue, 24 Jan 2012 14:58:58 +0000</lastBuildDate>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=3.1</generator>
		<item>
		<title>S3 Streaming With PHP</title>
		<link>http://www.missiondata.com/blog/systems-integration/49/s3-streaming-with-php/</link>
		<comments>http://www.missiondata.com/blog/systems-integration/49/s3-streaming-with-php/#comments</comments>
		<pubDate>Sat, 25 Nov 2006 18:45:24 +0000</pubDate>
		<dc:creator>carsonm</dc:creator>
				<category><![CDATA[Systems Integration]]></category>
		<category><![CDATA[linux]]></category>
		<category><![CDATA[php]]></category>
		<category><![CDATA[s3]]></category>

		<guid isPermaLink="false">http://blogs.missiondata.com/linux/49/s3-streaming-with-php/</guid>
		<description><![CDATA[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 [...]]]></description>
			<content:encoded><![CDATA[<p><a href="http://blogs.missiondata.com/author/steveny/">Steven</a> pointed out that someone was looking for a way to <a href="http://developer.amazonwebservices.com/connect/thread.jspa?threadID=12829&#038;tstart=0">stream to S3 using PHP</a> and said I should figure out how to get it going. Since he made a patch to let you <a href="http://blogs.missiondata.com/ruby/29/streaming-data-to-s3-with-ruby/">stream data with Ruby using S3</a> I figured I should do one for PHP. There may be better ways of doing this but since I&#8217;ve already done it with C using curl I figured that would be the fastest way.<br />
<span id="more-49"></span></p>
<p>First you will want to get the <a href="http://developer.amazonwebservices.com/connect/servlet/JiveServlet/download/24-12470-46948-845/s3.php">S3 PHP library from Amazon</a>. Then you add the following to the file:</p>
<pre>
<code>/**
 * 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=&gt;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-&gt;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-&gt;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-&gt;constructSig($stringToSign);

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

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

        $result = curl_exec ($curl_inst);

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

        curl_close($curl_inst);
}</code>
</pre>
<p>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.</p>
<p>Here are two examples of how to use it. First with a file:</p>
<pre>
<code>&lt;?php

include 's3.php';

class MyClass
{
  var $data;

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

$my_class_inst = new MyClass();

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

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

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

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

fclose($my_class_inst-&gt;data);

?&gt;</code>
</pre>
<p>Another example of streaming some random text:</p>
<pre>
<code>&lt;?php

include 's3.php';

class MyClass
{
  var $data;

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

$my_class_inst = new MyClass();

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

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

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

?&gt;</code>
</pre>
]]></content:encoded>
			<wfw:commentRss>http://www.missiondata.com/blog/systems-integration/49/s3-streaming-with-php/feed/</wfw:commentRss>
		<slash:comments>6</slash:comments>
		</item>
		<item>
		<title>Creating S3 URLs that expire using PHP</title>
		<link>http://www.missiondata.com/blog/systems-integration/57/creating-s3-urls-that-expire-using-php/</link>
		<comments>http://www.missiondata.com/blog/systems-integration/57/creating-s3-urls-that-expire-using-php/#comments</comments>
		<pubDate>Thu, 01 Jun 2006 11:50:24 +0000</pubDate>
		<dc:creator>carsonm</dc:creator>
				<category><![CDATA[Systems Integration]]></category>
		<category><![CDATA[php]]></category>
		<category><![CDATA[s3]]></category>

		<guid isPermaLink="false">http://blogs.missiondata.com/?p=57</guid>
		<description><![CDATA[After reading this post on the S3 forum I realized that other people are thinking about doing some of the same stuff I have. paolonew was looking for a way to for a way to create URLs to S3 objects that expired. I did this a while back when I was thinking about how to [...]]]></description>
			<content:encoded><![CDATA[<p>After reading <a href="http://developer.amazonwebservices.com/connect/thread.jspa?threadID=10726&#038;tstart=0">this post on the S3 forum</a> I realized that other people are thinking about doing some of the same stuff I have. paolonew was looking for a way to for a way to create URLs to S3 objects that expired. I did this a while back when I was thinking about how to host objects that I wanted to protect with some outside scheme. The confusion on the forum seemed to be about the timestamps used to expire the URL. For PHP it is fairly easy.</p>
<p>To clear up the expiration time issue I think these two steps are needed:</p>
<p>1) Keep in mind that the HTTP header dates <a href="http://www.w3.org/Protocols/rfc2616/rfc2616-sec3.html">must</a> be in GMT.<br />
2) The PHP function <a href="http://us3.php.net/manual/en/function.time.php">time()</a> returns the seconds since the epoch January 1 1970 00:00:00 GMT). Notice here this is in GMT as well.<br />
3) The HTTP Date header you see in a response from an S3 server is the time on that server. The machine you use to sign your request should be synced with that time. I think a good guess is that all the Amazon servers are synced with the atomic clock.</p>
<p>There isn&#8217;t much to securing a URL after you have that tucked away. Here is an example that will sign a URL so that it is valid for 60 seconds:</p>
<pre>
<code>&lt;?php

require_once('Crypt/HMAC.php');

echo getS3Redirect("/test.jpg") . "\\n";

function getS3Redirect($objectName)
{
  $S3_URL = "http://s3.amazonaws.com";
  $keyId = "your key";
  $secretKey = "your secret";
  $expires = time() + 60;
  $bucketName = "/your bucket";

  $stringToSign = "GET\\n\\n\\n$expires\\n$bucketName$objectName";
  $hasher =&amp; new Crypt_HMAC($secretKey, "sha1");
  $sig = urlencode(hex2b64($hasher-&gt;hash($stringToSign)));

  return "$S3_URL$bucketName$objectName?AWSAccessKeyId=$keyId&amp;Expires=$expires&amp;Signature=$sig";
}

function hex2b64($str)
{
    $raw = '';
    for ($i=0; $i &lt; strlen($str); $i+=2)
    {
        $raw .= chr(hexdec(substr($str, $i, 2)));
    }
    return base64_encode($raw);
}

?&gt;</code>
</pre>
<p>The hex2b64 function was pulled from the amazon S3 PHP example library.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.missiondata.com/blog/systems-integration/57/creating-s3-urls-that-expire-using-php/feed/</wfw:commentRss>
		<slash:comments>3</slash:comments>
		</item>
		<item>
		<title>Streaming data to S3 with ruby</title>
		<link>http://www.missiondata.com/blog/systems-integration/29/streaming-data-to-s3-with-ruby/</link>
		<comments>http://www.missiondata.com/blog/systems-integration/29/streaming-data-to-s3-with-ruby/#comments</comments>
		<pubDate>Wed, 29 Mar 2006 18:56:49 +0000</pubDate>
		<dc:creator>steveny</dc:creator>
				<category><![CDATA[Systems Integration]]></category>
		<category><![CDATA[ruby]]></category>
		<category><![CDATA[s3]]></category>

		<guid isPermaLink="false">http://blogs.missiondata.com/?p=29</guid>
		<description><![CDATA[One of the downsides of the ruby S3 example code is that it doesn&#8217;t support streaming of data (it loads the entire file into memory). It turns out, however, that all that is needed to stream data is a tweak to the &#8216;request&#8217; method in Net::HTTP. require 'net/http' require 'S3' require 'pp' # # Replace [...]]]></description>
			<content:encoded><![CDATA[<p>One of the downsides of the ruby S3 example code is that it doesn&#8217;t support streaming of data (it loads the entire file into memory).  It turns out, however, that all that is needed to stream data is a tweak to the &#8216;request&#8217; method in Net::HTTP.</p>
<pre>
  <code>require 'net/http'
require 'S3'
require 'pp'

#
# Replace the request method in Net::HTTP to sniff the body type
# and set the stream if appropriate
#
module Net
  class HTTP
    alias __request__ request

    def request(req, body = nil, &amp;block)
      if body != nil &amp;&amp; body.respond_to?(:read)
        req.body_stream = body
        return __request__(req, nil, &amp;block)
      else
        return __request__(req, body, &amp;block)
      end
    end
  end
end

#
# Connect to s3 using the ruby API provided by Amazon
#
conn = S3::AWSAuthConnection.new("[PUBLIC]", "[PRIVATE]", false)

#
# Stream a testfile to S3
#
open("testfile") do |stream|
  pp response = conn.put('BUCKET_NAME',
                         "testfile",
                         stream,
                         {
                           "x-amz-acl" =&gt; "public-read",
                           "Content-Type" =&gt; "text/plain",
                           "Content-Length" =&gt;  FileTest.size("testfile").to_s
                         }
                        )
end

#
# Send a testfile in memory to S3
#
pp response = conn.put('BUCKET_NAME',
                       "testfile",
                       File.read('testfile'),
                       {
                         "x-amz-acl" =&gt; "public-read",
                         "Content-Type" =&gt; "text/plain"
                       }
                      )

  </code>
</pre>
<p>A few notes about the code</p>
<ul>
<li> When streaming you have to supply the &#8216;Content-Length&#8217; header
<li> I had an error about S3.rb calling strip on non-strings, I changed line 49 to &#8216;interesting_headers[lk] = value.to_s.strip&#8217;
<li> Make sure you replace PUBLIC, PRIVATE, and BUCKET_NAME with appropriate values
</ul>
]]></content:encoded>
			<wfw:commentRss>http://www.missiondata.com/blog/systems-integration/29/streaming-data-to-s3-with-ruby/feed/</wfw:commentRss>
		<slash:comments>6</slash:comments>
		</item>
		<item>
		<title>Offloading web traffic using Amazon&#8217;s S3 service</title>
		<link>http://www.missiondata.com/blog/systems-integration/27/offloading-web-traffic-using-amazons-s3-service/</link>
		<comments>http://www.missiondata.com/blog/systems-integration/27/offloading-web-traffic-using-amazons-s3-service/#comments</comments>
		<pubDate>Tue, 28 Mar 2006 17:43:45 +0000</pubDate>
		<dc:creator>steveny</dc:creator>
				<category><![CDATA[Systems Integration]]></category>
		<category><![CDATA[ruby]]></category>
		<category><![CDATA[s3]]></category>

		<guid isPermaLink="false">http://blogs.missiondata.com/?p=27</guid>
		<description><![CDATA[We have a couple of fairly high traffic sites that have large images designed to be used for desktop backgrounds. To save a bit of bandwidth, we decided to give Amazon&#8217;s S3 webservice a spin. Signing up was fairly painless. They required a credit card (so they could bill us $.15/G storage and $.20/G transfer). [...]]]></description>
			<content:encoded><![CDATA[<p>We have a couple of fairly high traffic sites that have large images designed to be used for desktop backgrounds.  To save a bit of bandwidth, we decided to give <a href='http://aws.amazon.com/s3'>Amazon&#8217;s S3 webservice</a> a spin.</p>
<p>Signing up was fairly painless.  They required a credit card (so they could bill us $.15/G storage and $.20/G transfer).  After I signed up I quickly received an email that contained a link to my public and secret keys.  </p>
<p>This is a fairly new service and the client tools are just getting started.  For my purposes of uploading several images, I decided to use <a href='http://www.jroller.com/page/silvasoftinc'>jSh3ll</a> to &#8216;browse&#8217; my S3 storage and a custom ruby script to upload a large amount of files.</p>
<p>After downloading and installing jSh3ll, I created my first bucket:<br />
<span id="more-27"></span></p>
<pre>
<code class='console'>jSh3ll&gt; bucket www.hpnotiq.com
Bucket set to 'www.hpnotiq.com'
jSh3ll&gt; createbucket
Created bucket 'www.hpnotiq.com'</code>
</pre>
<p>I then hacked out a quick ruby script using <a href="http://developer.amazonwebservices.com/connect/entry.jspa?externalID=135&#038;categoryID=47">the example ruby S3 library</a> to upload all the image files I wanted to store on S3.</p>
<pre>
  <code>#!/usr/bin/env ruby

require 'S3'
require 'pp'

conn = S3::AWSAuthConnection.new("&lt;YOUR PUBLIC KEY&gt;", "&lt;YOUR PRIVATE KEY&gt;", false)

Dir["images/*.jpg"].each do |filename|
  basename = filename.split('/')[-1]
  pp response = conn.put('www.hpnotiq.com',
                         "images/wallpapers/#{basename}",
                         File.new(filename).read,
                         {
                           "x-amz-acl" =&gt; "public-read",
                           "Content-Type" =&gt; "image/jpeg"
                         }
                        )
end
  </code>
</pre>
<p>A few notes about the code:</p>
<ul>
<li> We decided to use the domain name for the bucket and the directory+file for the object id
<li> The S3 ruby libraries don&#8217;t stream, so the whole file is loaded into memory and put on the server
<li> The hash at the end sets some headers for the &#8216;put&#8217;.  The first tells S3 that this file can be read publicly.  The second is the content type for the file(which will be set in the header when the file is downloaded)
</ul>
<p>Back to JSh3ll to see what&#8217;s there:</p>
<pre>
  <code class='console'>jSh3ll&gt; list
Item list for bucket 'www.hpnotiq.com'
key=images/wallpapers/blackdrink_1024.jpg, owner=steveny, size=75625 bytes, last modified=Tue Mar 28 11:54:57 EST 2006
key=images/wallpapers/blackdrink_800.jpg, owner=steveny, size=47251 bytes, last modified=Tue Mar 28 11:54:59 EST 2006
key=images/wallpapers/bottle_1024.jpg, owner=steveny, size=56590 bytes, last modified=Tue Mar 28 11:55:00 EST 2006
  </code>
</pre>
<p>Now that all the files are on the service, I just needed to write an apache rewrite rule to redirect people to the images&#8217; new location:</p>
<pre>
  <code>RewriteEngine on
RewriteRule ^/images/wallpapers/(.*)$ http://s3.amazonaws.com/www.hpnotiq.com/images/wallpapers/$1 [R,L]
  </code>
</pre>
<p>Bringing up my browser and looking at the headers, we can see where the request gets redirected to s3.amazonaws.com and the Content-Type is set correctly:</p>
<pre>
  <code>GET /images/wallpapers/blackdrink_1024.jpg HTTP/1.1

Host: www.hpnotiq.com
User-Agent: Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.7.12) Gecko/20051010 Firefox/1.0.7 (Ubuntu package 1.0.7)
Accept: text/xml,application/xml,application/xhtml+xml,text/html;q=0.9,text/plain;q=0.8,image/png,*/*;q=0.5
Accept-Language: en-us,en;q=0.5
Accept-Encoding: gzip,deflate
Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7
Keep-Alive: 300
Connection: keep-alive
Pragma: no-cache
Cache-Control: no-cache

HTTP/1.x 302 Found
Date: Tue, 28 Mar 2006 17:49:27 GMT
Server: Apache
Location: http://s3.amazonaws.com/www.hpnotiq.com/images/wallpapers/blackdrink_1024.jpg
Content-Length: 326
Keep-Alive: timeout=2, max=100
Connection: Keep-Alive
Content-Type: text/html; charset=iso-8859-1

----------------------------------------------------------

http://s3.amazonaws.com/www.hpnotiq.com/images/wallpapers/blackdrink_1024.jpg

GET /www.hpnotiq.com/images/wallpapers/blackdrink_1024.jpg HTTP/1.1
Host: s3.amazonaws.com
User-Agent: Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.7.12) Gecko/20051010 Firefox/1.0.7 (Ubuntu package 1.0.7)
Accept: text/xml,application/xml,application/xhtml+xml,text/html;q=0.9,text/plain;q=0.8,image/png,*/*;q=0.5
Accept-Language: en-us,en;q=0.5
Accept-Encoding: gzip,deflate
Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7
Keep-Alive: 300
Connection: keep-alive
Pragma: no-cache
Cache-Control: no-cache

HTTP/1.x 200 OK
x-amz-id-2: GxsekKOL2jdljb5K6/RWPlswvBhbNjYrP8klHs2IXGqwZcjzRQ3FIsEhPo/L/Gfe
x-amz-request-id: 6E6912FF565D7B04
Date: Tue, 28 Mar 2006 17:49:29 GMT
Last-Modified: Tue, 28 Mar 2006 16:54:57 GMT
Etag: "a58c016d36905ba89cf17ea99a574cb3"
Content-Type: image/jpeg
Content-Length: 75625
Server: AmazonS3
  </code>
</pre>
]]></content:encoded>
			<wfw:commentRss>http://www.missiondata.com/blog/systems-integration/27/offloading-web-traffic-using-amazons-s3-service/feed/</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
	</channel>
</rss>

