This patch was created to increase Zend_Service_Amazon_S3_Stream's usability
inside WordPress. After this initial proof-of-concept, my intention has been
to reimplement this in an alternate way (e.g. a subclass). Rather than continue
to keep the code to myself until that happens, I am releasing it into the wild.

For more information, see this blog post:

http://sixohthree.com/1575/saving-wordpress-media-to-amazon-s3-teaser

Adam Backstrom
4 November 2011

diff --git a/Service/Amazon/S3.php b/Service/Amazon/S3.php
index 4262e68..d503cca 100755
--- a/Service/Amazon/S3.php
+++ b/Service/Amazon/S3.php
@@ -952,7 +952,7 @@ class Zend_Service_Amazon_S3 extends Zend_Service_Amazon_Abstract
          */
         require_once 'Zend/Service/Amazon/S3/Stream.php';
 
-        stream_register_wrapper($name, 'Zend_Service_Amazon_S3_Stream');
+        stream_register_wrapper($name, 'Zend_Service_Amazon_S3_Stream', STREAM_IS_URL);
         $this->registerAsClient($name);
     }
 
diff --git a/Service/Amazon/S3/Stream.php b/Service/Amazon/S3/Stream.php
index f718e21..d492be8 100755
--- a/Service/Amazon/S3/Stream.php
+++ b/Service/Amazon/S3/Stream.php
@@ -183,6 +183,8 @@ class Zend_Service_Amazon_S3_Stream
      */
     public function stream_read($count)
     {
+        static $_cacheFile = array();
+
         if (!$this->_objectName) {
             return false;
         }
@@ -192,6 +194,14 @@ class Zend_Service_Amazon_S3_Stream
             $count = $this->_objectSize - $this->_position;
         }
 
+        if( $_cacheFile[$this->_objectName] !== null && strlen($this->_objectBuffer) == 0 ) {
+            $fp = $_cacheFile[$this->_objectName];
+            fseek($fp, 0);
+            while( !feof($fp) ) {
+                $this->_objectBuffer .= fread($fp, 131072);
+            }
+        }
+
         $range_start = $this->_position;
         $range_end   = $this->_position + $count - 1;
 
@@ -199,17 +209,34 @@ class Zend_Service_Amazon_S3_Stream
         // OR, the range end position plus 1 is greater than the size of the current
         // object buffer
         if ($this->_objectBuffer === null  ||  $range_end >= strlen($this->_objectBuffer)) {
+            $range_end = $range_start + 262143;
+
             $headers = array(
                 'Range' => "bytes=$range_start-$range_end"
             );
 
+            //error_log( "reading $range_start-$range_end for $this->_objectName" );
             $response = $this->_s3->_makeRequest('GET', $this->_objectName, null, $headers);
 
             if ($response->getStatus() == 206) { // 206 Partial Content
+                if (strlen($this->_objectBuffer) < $this->_position) {
+                    $this->_objectBuffer = str_pad($this->_objectBuffer, $this->_position);
+                }
+
                 $this->_objectBuffer .= $response->getBody();
             }
         }
 
+        if( $_cacheFile[$this->_objectName] === null && strlen($this->_objectBuffer) == $this->_objectSize ) {
+            $tmpfile = tempnam('/tmp', 's3tmp');
+            $fp = fopen($tmpfile, 'w');
+            fwrite($fp, $this->_objectBuffer);
+            fclose($fp);
+
+            $_cacheFile[$this->_objectName] = fopen($tmpfile, 'r');
+            unlink($tmpfile);
+        }
+
         $data = substr($this->_objectBuffer, $this->_position, $count);
         $this->_position += strlen($data);
         return $data;
@@ -387,6 +414,15 @@ class Zend_Service_Amazon_S3_Stream
      */
     public function mkdir($path, $mode, $options)
     {
+        $name_part = $this->_getNamePart($path);
+
+        if( false !== strpos( $name_part, '/' ) ) {
+            $headers = array(
+                Zend_Service_Amazon_S3::S3_CONTENT_TYPE_HEADER => 'application/x-directory',
+            );
+            return $this->_getS3Client($path)->putObject($name_part, '', $headers);
+        }
+
         return $this->_getS3Client($path)->createBucket(parse_url($path, PHP_URL_HOST));
     }
 
@@ -457,9 +493,15 @@ class Zend_Service_Amazon_S3_Stream
            $info = $this->_getS3Client($path)->getInfo($name);
 
         if (!empty($info)) {
+            if($info['type'] == 'application/x-directory') {
+                $stat['mode'] |= 040000;
+                $stat['mode'] ^= 0100000;
+            }
             $stat['size']  = $info['size'];
             $stat['atime'] = time();
             $stat['mtime'] = $info['mtime'];
+        } else {
+            return false;
         }
 
         return $stat;
