Blog

Amazon S3 PHP Class

Monday October 22, 2007

This class is a standalone Amazon S3 REST implementation for PHP 5 (using CURL), that supports large file uploads and doesn't require PEAR.


Checkout latest source:
svn checkout http://amazon-s3-php-class.googlecode.com/svn/trunk/ s3-php

Download source:

Example usage (also see example.php and the class documentation):


$s3 = new S3('accessKey', 'secretKey');
$s3->putBucket('bucket', S3::ACL_PUBLIC_READ);
$s3->putObjectFile('file.doc', 'bucket', 'docs/file.doc', S3::ACL_PUBLIC_READ);
$s3->deleteObject('bucket', 'docs/file.doc');


Writing and reading resources:


# Upload an object from a resource (requires bytesize):
$s3->putObject($s3->inputResource(fopen($file, 'rb'), filesize($file)), $bucketName, $uploadName, S3::ACL_PUBLIC_READ);

# Download an object to a resource:
$s3->getObject($bucketName, $uploadName, fopen($savefile, 'wb'));


Known Issues:

More Information:

Changelog

  • 0.3.9 (2008-12-23):
    Added $returnCommonPrefixes parameter to getBucket() to include CommonPrefixes
  • 0.3.8 (2008-12-16):
    Support for spaces and special characters in URIs
  • 0.3.7 (2008-11-27):
    Support for CloudFront and changes to setBucketLogging()
  • 0.3.6 (2008-10-23):
    PHPDoc comment updates
  • 0.3.5 (2008-10-08):
    Added getAuthenticatedURL() and an ACL parameter to copyObject()
  • 0.3.4 (2008-09-11):
    Small updates and support for 0 byte data
  • 0.3.3 (2008-07-30):
    Fixed minor getBucket() bugs, added delimiter
  • 0.3.2 (2008-07-12):
    Fixed ?logging bug and changed putObject() to accept request headers (thanks to Angelo Mandato for providing details)
  • 0.3.1 (2008-06-20):
    Added copyObject() and updated README.txt
  • 0.2.9 (2008-06-20):
    Added getBucketLocation() and a location parameter to putBucket() for EU buckets
  • 0.2.8 (2008-06-06):
    Added sort() for AMZ meta headers (thanks Malone)
  • 0.2.7 (2008-05-30):
    Commented out uncommented var_dump() (thanks Chris)
  • 0.2.6 (2008-05-29):
    Replaced isset() with array_key_exists() since isset() was ignoring null parameters (thanks Joe)
  • 0.2.5 (2008-05-29):
    Added support for ?location and ?torrent parameters (thanks Joe)
  • 0.2.4 (2008-05-27):
    Fix for ?prefix bug (thanks Lucas)
  • 0.2.3 (2008-05-04):
    Small bug fixes, updated examples and comments, moved source to SVN
  • 0.2.2 (2008-05-02):
    Added S3 wrapper example
  • 0.2.1 (2008-04-28):
    First 0.2 beta (0.1 development discontinued)

Amazon S3™ is a trademark of Amazon.com, Inc. or its affiliates.

Comments

  1. hi I really like your s3 php class, very clean abstracted code. Do you have any ideas on streaming? instead of put, I saw a couple different sites, with examples, like, this one, http://www.missiondata.com/blog/linux/49/s3-streaming-with-php/

    Please let me know if you enhance your class to do this

    -- Jimmy Ho on 30 November 2007

  2. How would one go about uploading a folder? Apart from putObjectFile into a folder subdirectory? Is there a way to take a whole folder, its files, and subfolders at once?

    -- Scott on 26 January 2008

  3. Just downloaded and looked at your source, and I like it… The example.php works perfectly. Thank you very much!

    -- puzz on 5 February 2008

  4. Hey! Awesome script, very useful. I miss a couple of things though:

    A listBuckets function, to check which buckets exists.

    A checkObject function that checks if an object with the (file)name exists, without having to get a whole object.

    Maybe these things can be done allready, but I can’t see it. Thanks!

    -- Hans on 7 February 2008

  5. thanks for the class, been trying to find one that didn’t use pear. New function suggestion would be a getBuckets() function to get a listing of all the buckets you have created.

    -- Todd on 15 February 2008

  6. @Jimmy: Streaming like you mentioned probably is possible, will have a look at it sometime.

    @Hans: There’s the listBuckets() function you wanted. I’m not sure if a checkObject() could be implemented.

    @Scott: You’d need to iterate through the directory uploading each file.

    @Todd: Yeah, the PEAR requirement is what made me write this class. listBuckets() there for you too!

    Hope you all find it useful. If you use it for something cool, drop me a line and let me know!

    -- Don on 18 February 2008

  7. Good stuff, thanks Don!

    -- Hans on 19 February 2008

  8. RE: checkObject()
    there is a function in the api called HEAD that will fetch the metadata of a file. (Using Rest API > Operations on objects > HEAD object) That may give you what you need as it will give a file not found error if the name doesn’t exist.

    And Don, thanks for the update

    -- Todd on 22 February 2008

  9. Some kind of streaming large files would be a great addition to this script. I seem to get time outs when trying files even as ‘small’ as 13-15mb. Perhaps that’s more of a local (hosting) issue than scripting?

    -- Hans on 25 February 2008

  10. @Hans: Are you getting timeouts on getObject() or putObjectFile()? The putObjectFile() method doesn’t place the entire file into memory when uploading, but getObject() on the other hand does. I haven’t had any problems uploading files as big as 150MB and it usually transfers at about 40kb/s.

    -- Don on 25 February 2008

  11. I’m testing this on my localhost and it’s doing fine except for the the fact that I can’t get it to upload a 250k image. I can upload an image that is around 60k and smaller but I am not sure why I cannot upload this particular file. Do you have any ideas or suggestions?

    -- Rich on 28 February 2008

  12. tis is nice, is there a way to create folders?

    -- Fuerchti on 28 February 2008

  13. @Don: The timeouts occur with putObjectFile(). Have you tried the script on a shared hosting space? It might be something there..

    Is there someway I could debug this?

    Thanks for your support!

    -- Hans on 29 February 2008

  14. When I try to upload large files it will just stop after a couple minutes. It gives no error.

    -- Nic on 29 February 2008

  15. Thanks for this, haven’t used to the class yet but intend to. Just noticed a spelling error with method getObjecInfo – shouldn’t it be getObjectInfo?

    -- Adam on 10 March 2008

  16. @Adam: Thanks mate!

    @Nic, @Hans, @Rich: Not sure why you’re experiencing timeouts, might require a setopt for CURLOPT_TIMEOUT or something… I’ll look into it sometime.

    -- Don on 10 March 2008

  17. Example doesn’t work for me. Error as follows:

    sweeney@desktop% ./example.php

    Parse error: parse error, expecting `’(’‘ in /Users/sweeney/s3/s3-php5-curl/example.php on line 39
    sweeney@desktop% php -v
    PHP 5.2.5 (cli) (built: Mar 11 2008 12:32:56)
    Copyright © 1997-2007 The PHP Group
    Zend Engine v2.2.0, Copyright © 1998-2007 Zend Technologies
    sweeney@desktop%

    Environment is MacOS 10.4; hopefully this is a simple problem. Example.php is as delivered, but with real key values.

    -- Tony Sweeney on 11 March 2008

  18. @Tony: Works fine for me. Perhaps you modified the file without noticing?

    -- Don on 11 March 2008

  19. @Don: problem was pilot error — had stale PHP version laying around somewhere.

    Tony.

    -- Tony Sweeney on 11 March 2008

  20. don, great script! everything is working quite nicely. quick question — how do you get a private object back from S3 from the command line?

    -- Michael on 13 March 2008

  21. @Michael: Use getObject()

    -- Don on 13 March 2008

  22. Great work… lean and mean. I echo what everyone else said about the PEAR bloat.

    But… you didn’t mention that it needed Simple XML. Worse, you used an @ in the call: @simplexml_load_string($data\[1\]) : $data\[1\];

    (Backslashes are because your web site want to make superscripts from the array indexes.)

    Result was absolute silence.

    Ouch!

    But, it’s such a great piece of work that I forgive you.

    —Marc

    -- Marc Rochkind on 14 March 2008

  23. I Studied your script it was nice.
    I have one question for you there a way to create folders inside bucket.
    I’ll be thankful if you reply me.

    -- Bivek on 25 March 2008

  24. nice work. Good clean code and easy to use.

    -- flungabunga on 26 March 2008

  25. Can any one tell me why use cURL instead of PEAR HTTP ?

    -- Akshar on 31 March 2008

  26. @Bivek: Not sure – and you’re not the first person to ask. Haven’t tried this yet but it’s probably possible.

    @Ashkar: The PEAR requirement everyone else is talking about is for the HMAC-SHA1 key generation. CURL simply provides a nice HTTP client interface.

    -- Don on 31 March 2008

  27. Thanks for the class, works great. Does the putObjectFile place files in folders under bucket? Only seems to work putting files in the main bucket.

    Thanks!

    -- Del on 5 April 2008

  28. Need help while creating a bucket. No matter what bucketName I give it sends same error Unable to create bucket (it may already exist and/or be owned by someone else)<br><br> Does anyone have a solution? Thanks in advance

    -- Sachin on 7 April 2008

  29. Thanks for writing this! Question – if a bucket has more than 1000 objects in it, how do you list them all?

    -- fletch on 11 April 2008

  30. This doesn’t work on 32 bit systems for files larger than ~2.1 GB. There are two main reasons: 1) php is_file function fails on files over this size 2) php md5_file function fails on files over this size

    -- ChrisN on 13 April 2008

  31. When I’m trying to upload a image with the example.php, then I can’t VIEW it in Firefox (Firefox will download the file), but I can view it in Safari and Opera. What’s wrong?

    -- Anon on 13 April 2008

  32. Well done and thanks a lot. Will have to backport it to PHP 5.1.x as not all servers in our cluster are on 5.2 yet.. but definitely a perfect class – and all that without PEAR. :-)

    -- Rob Kaper on 18 April 2008

  33. Just wondering if anyone else is experiencing that the API requests take upwards of a minute to complete?

    The slowness comes from this line in S3Object::getResponse:
    $data = explode(”\r\n\r\n”, curl_exec($curlReq), 2);

    I have PHP 5.2.5, and curl.

    Perhaps it is AWS’s fault? Any other ideas?

    -- Jonathan Lipps on 24 April 2008

  34. I love your S3 package, but upon using it, I noticed that it didn’t print error messages, didn’t handle truncation (over 1000 keys in a bucket). I’ve modified it to do so, and if you’re interested, I’d like to send it to you so others can benefit from this. Can you email me how I can do this? Thanks.

    -- Corey on 26 April 2008

  35. @Sachin, @Jonathan, @Corey, @fletch: Try the 0.2 beta :)

    @Rob: Might even work on 5.1 I don’t think there is any reason it wouldn’t.

    -- Don on 29 April 2008

  36. GREAT WORK!!!

    Thanks from Spain.

    The best and simplest class for S3

    -- EduT on 2 May 2008

  37. Great work! I use this script in some of my personal projects and it works great.

    One of my projects required the use of PHP4 without any PEAR packages and figuring out how to create HMAC signatures was difficult. In case anyone else needs to do this, I documented my results here:

    http://blog.raamdev.com/2008/05/03/amazon-s3-hmac-signatures-without-pear-or-php5/

    -- Raam Dev on 4 May 2008

  38. Does svn co require user/password?

    -- Jason on 5 May 2008

  39. I am using latest beta release 0.2.3 I am getting following error :

    ==
    Warning: S3::putBucket(Rajantestbucket): [SignatureDoesNotMatch] The request sig
    nature we calculated does not match the signature you provided. Check your key a
    nd signing method. in C:\xampp\htdocs\S3.php on line 188
    =====
    I have checked both Accesskey and Secretkey, they seems to be ok.

    Do you know where I am going wrong ?

    ——-

    <?php

    require_once(‘s3.php’);

    $bucketName = “Rajantestbucket”;
    $s3 = new S3(“ABCDEFGHI”, “XYHDSDSJHDKDSLKDSL”);
    if( $s3->putBucket( $bucketName, S3::ACL_PUBLIC_READ) ) echo “Bucket : “ . $bucketName . “Created”;

    echo “S3::listBucket(): “.print_r($s3->listBuckets(),1). “\n”;
    // $s3->putObjectFile(‘file.doc’, ‘bucket’, ‘docs/file.doc’, S3::ACL_PUBLIC_READ);
    // $s3->deleteObject(‘bucket’, ‘docs/file.doc’);

    ?>

    -- Rajan on 7 May 2008

  40. @Rajan,

    From the AWS documentation:

    Bucket names can only contain lowercase letters, numbers, periods (.), underscores (_), and dashes (-).

    -- Don on 7 May 2008

  41. Which is the best method for uploading a file from a form?

    Put an object from a resource? from a file? from a string?

    Thanks!

    The file is uploaded first to my server using multipart form data.

    -- Edu on 9 May 2008

  42. Great class. Love the putObjectFile method. I used to create an object, check for type and upload the object…this method does it all. Very well written. Thank you!!

    -- Ren on 13 May 2008

  43. Hi,

    you wrote:

    @Scott: You’d need to iterate through the directory uploading each file.

    can you explain this. i’m sorry, but i don’t understand how to make this iteration – in what place.

    -- Dawid Gatti on 24 May 2008

  44. OK i figure it out. if you are wondering how to create a directory in a bucket this is how you do it.

    $s3->putObjectFile($uploadFile, $bucketName, ‘io/’.$uploadFile, S3::ACL_PUBLIC_READ)

    the “io” is the new directory.

    -- Dawid Gatti on 24 May 2008

  45. Hello,

    I’ve got your script working, and I like it a lot. Thanks!

    But whenever I try to specify a $prefix to getBucket(), I get an error saying “[SignatureDoesNotMatch] The request signature we calculated does not match the signature you provided. Check your key and signing method.”

    I tried looking through your code, but I can’t find what might cause this. Do you know why this might occur?

    -- lucas on 27 May 2008

  46. @Lucas: Try the latest source

    -- Don on 27 May 2008

  47. Don, i have the same problem whit the latest code.

    the funny thing is that i can upload jpg, wav, zip, pdf, txt, cab

    but i can’t upload mov, mp3, iso

    of course i didn’t test all the formats but you can see that something is wrong. the size doesn’t mater. i can upload a bigger jpg file and have no problem, but uploading a smaller mp3, mov etc generate this problem about signature.

    any idea?

    -- Dawid Gatti on 28 May 2008

  48. around line 773, I change a little for getAccessControl to work properly. if (isset($this->parameters[‘acl’]) || $this->parameters[‘acl’]==null) { $this->resource .= $query; }

    -- joe on 28 May 2008

  49. my last post has some bug too. To make the object work, some changes are still needed to get the correct resource value for the lasted version. I change to the following and it seems working:
    $appendArray = array(‘acl’,‘location’,‘torrent’); foreach($appendArray as $append) { if(isset($this->parameters[$append])) { $this->resource .= “?$append”; } }

    -- joe on 28 May 2008

  50. in the setAccessControlPolicy(),

    the new S3Request has a bug missing $uri;
    $rest = new S3Request(‘PUT’, $bucket, $uri);

    also, it seems I have to set acl value as 1 to make it work and attached to resource instead of null.
    $rest->setParameter(‘acl’, 1);

    -- joe on 29 May 2008

  51. @Joe: Thanks for the heads up, didn’t notice that isset() would ignore null array values there…

    -- Don on 29 May 2008

  52. I still have the same problem whit 2.6

    string(11) “test.foo.pl” string(15) “/klawaitura.mov” string(27) “/test.foo.pl/klawaitura.mov” string(51) “https://test.foo.pl.s3.amazonaws.com/klawaitura.mov”
    Warning: S3::putObject(): [SignatureDoesNotMatch] The request signature we calculated does not match the signature you provided. Check your key and signing method. in /home/users/dawidgatti2/public_html/s3/S3up.php on line 312

    -- Dawid Gatti on 30 May 2008

  53. WOW!, this is EXACTLY what i’ve been looking for! I just uploaded a 14mb file at 120kb/s (which is my max), and it only took me about 30 seconds to look at the example to find out how, thanks!

    -- Nick on 30 May 2008

  54. Implemented in less that an hour…awesome work. Quick question though: when I call $s3->putObjectFile(), why does it print out the filename stuff afterwards? EG:

    string(12) “bucket” string(25) “/folder1/folder2/file.flv” string(38) “/bucket/folder1/folder2/file.flv” string(62) “https://bucket.s3.amazonaws.com/folder1/folder2/file.flv”

    With bucket and folder being the actual buckets and folders? Is there a print function happening somewhere upon successful upload? Thanks!

    -- Chris on 30 May 2008

  55. @Chris: There’s a var_dump in there for debugging, just comment it out.

    -- Don on 30 May 2008

  56. Hello

    I am trying to delete a bucket which is listed as

    b96a4960ce964987b1a381bb35ca33-Test

    but went I use the $s3->deleteBucket command all I get is access-denied or can not find bucket. What am I doing wrong

    If I create a buket like rob123 that delete fine it just can not find out how to delete these ones. Please help

    -- Rob on 3 June 2008

  57. @Rob: “Bucket names can only contain lowercase letters, numbers, periods (.), underscores (_), and dashes (-).”

    -- Don on 4 June 2008

  58. Hi, I must thank you for this because Amazon’s documentation is useless, I was up and running with this in no time. You have been a huge help!

    I’m having a problem though which I was wondering if you had experienced, the script hangs when uploading very small objects, such as a 1×1 spacer gif, or a browser-dependent stylesheet with just a line or two. The file does appear in the bucket, but then it dies.

    I added a CURL timeout which seemed sensible anyway but to no avail.

    -- SJ on 4 June 2008

  59. Sorry Don

    That is not the case. As this is the program that created them was Jungle Disk. Therefore remember that I use the list option to find the, out.

    Therefore they are already created I wish to delete them.

    Rob

    -- Rob on 5 June 2008

  60. I haven’t changed any code and i’m getting this error whenever i navigate to the s3.php page

    Parse error: syntax error, unexpected T_CONST, expecting T_OLD_FUNCTION or T_FUNCTION or T_VAR or ‘}’ in /homepages/31/d204952132/htdocs/iarp/scripts/s3/S3.php on line 37

    Please add me on MSN iarp@cogeco.ca if you’ve anyidea what’s wrong.

    -- Ian on 6 June 2008

  61. Great class, it’s really saved me a lot of time.

    But I was getting [SignatureDoesNotMatch] errors when I sent certain metadata. The docs say you have to “Sort the collection of headers lexicographically by header name” before signing them. So I added “sort($amz);” before line 791 of S3.php, and that fixed the problem.

    -- malone on 6 June 2008

  62. @Ian: This class requires PHP 5 and you are using PHP 4.

    @Malone: Thanks for the heads up.

    -- Don on 6 June 2008

  63. Wow, quick update!

    I’m also having problems uploading empty (zero byte) files. I’m creating a backup system and it’s important that those files are stored, even though they have no content.

    I’ve managed to upload empty files using another tool, so it seems to be possible. But when I try to do it with your class I get [NotImplemented]. Also, if i try to upload an empty string it fails silently without an error.

    -- malone on 6 June 2008

  64. @Malone: I’m not sure about that…  Could have something to do with CURL…  Guess one
    option would be to insert some sort of single byte character into it?

    -- Don on 6 June 2008

  65. Works beautifully, many thanks :)

    How hard would it be to add in a parameter to putBucket to specify the location of the bucket? Right now the only possibility is EU (for Europe), with US being the default if you don’t specify (or does ‘US’ work as a location?), but it doesn’t hurt to plan ahead.

    I’ve only taken a cursory glance at the guts of S3 and S3Request so I’m not sure where you’d have to integrate this. If you were using PEAR/HTTP_Request, you’d use the addPutData method of HTTP_Request to add the appropriate snippet of XML to the put bucket request.

    I’ll look into it more tomorrow and see what I can figure out, but I thought I’d see if you know offhand.

    -- Heron on 11 June 2008

  66. Firstly, awesome class – very useful :)

    A little suggestion, if you wish, in the getBucket() function you could add the delimiter parameter as another argument, i’ve done this successfully for a project. Also in the initial checks within that function you have them all checking to see if $prefix !== ‘’ should this not be a check that each argument is not an empty string?
    e.g. you have
    if ($maxKeys !== null && $prefix !== ‘’)
    should it not be
    if ($maxKeys !== null && $maxKeys !== ‘’)

    Thanks again for a great class.

    -- Si on 13 June 2008

  67. We have a client who’s phpBB forums receive a large number of hits constantly. The traffic isn’t so much of a concern, but the storage space is. Has anyone used this with the aforementioned software to store attachments? If so, we’d love to hear from you.

    Karl

    -- Mothership on 16 June 2008

  68. @Karl: you’d have to modify phpBB to do that. It wouldn’t be very difficult, but it would take a bit of time (to track down everywhere that would need to be changed, if nothing else).

    -- Heron on 16 June 2008

  69. I’ve figured out how to specify Europe as the new bucket location, so I’ll tell you what I’ve added to make it work.

    First I added an array, $extradata, as another private member variable of S3Request. Then I added a method addExtraData($data) similar in spirit to setHeader.

    Next I added the following to the else statement in the ‘PUT’ case of the ‘request types’ switch in getResponse(), just after the CURLOPT_CUSTOMREQUEST line: [Sorry, the formatting doesn’t work well here.]

    $strdata = ‘’;
    foreach($extradata as $data)
    {
    $strdata .= $data;
    }

    if(strlen($strdata) > 0)
    {
    $temp = tmpfile();
    fwrite($temp, $strdata);
    curl_setopt($curl, CURLOPT_INFILE, $temp);
    curl_setopt($curl, CURLOPT_INFILESIZE, strlen($strdata));
    }

    Finally, I added a third parameter to S3::putBucket, $europe, which defaults to false. Just before calling $rest->getResponse(), I do:

    if($europe)
    {
    $data = ‘<CreateBucketConfiguration>\n’;
    $data .= ‘ <LocationConstraint>EU</LocationConstraint>\n’;
    $data .= ‘</CreateBucketConfiguration>’;
    $rest->addExtraData($data);
    }

    Next I plan on seeing whether I can make listBuckets’ detailed return indicate whether a bucket is in Europe or not. I’ll let you know if/when I do.

    Hope this helps.

    -- Heron on 19 June 2008

  70. @Heron: Excellent :)  I’ve been meaning to add this in for some time now.  There is an
    easy way to do this with 0.2.x…

    But to get a bucket location you can grab /bucket/uri?location – afaik there
    isn’t a way to do it with listBuckets()…

    -- Don on 19 June 2008

  71. You are correct, there is unfortunately no way to get this information in listBuckets.

    The changelog says the class supports the ?location and ?torrent parameters. How do I use them?

    Specifically I’m trying to figure out the location of a bucket. The request looks like this, according to Amazon:

    GET /?location HTTP/1.1
    Host: quotes.s3.amazonaws.com
    Date: Tue, 09 Oct 2007 20:26:04 +0000
    Authorization: AWS 1ATXQ3HHA59CYF1CVS02:JUtd9kkJFjbKbkP9f6T/tAxozYY=

    How do I get this set up with your s3 class? The getObjectInfo function doesn’t work, as it sends a HEAD request rather than a GET request as specified by Amazon. I think I’ll write another function to get this information until you have a moment to show me how to do it.

    -- Heron on 20 June 2008

  72. @Heron: I’ve added a getBucketLocation() method and a 3rd location parameter to putBucket().

    But you’re right the getObjectInfo() HEAD request wouldn’t work for ?location.

    -- Don on 20 June 2008

  73. Awesome, that’s perfect.

    Maybe next (if you’re open to suggestions or requests) you could add copy and rename methods for objects. Amazon lets you issue a COPY request to copy a file from one bucket+filename to another bucket+filename; that would make it easy for me to maintain European mirrors of my US data without having to upload the data twice.

    You’d have to implement rename as a COPY (to the new file name, same bucket) and a DELETE (old file name).

    -- Heron on 20 June 2008

  74. @Heron: Added copyObject() :) Didn’t even know that was possible, thanks for the heads up!

    -- Don on 20 June 2008

  75. Awesome :) I’ll try it out at work tomorrow.

    -- Heron on 20 June 2008

  76. I used copyObject to implement a ‘rename’ function in my little interface, since I don’t actually need to be able to copy objects right now.

    Works like a charm :)

    -- Heron on 23 June 2008

  77. Nice work. Works very well. You made my day! ;]

    -- Ronny on 24 June 2008

  78. Don,

    I need to be able to modify the access permissions of an existing bucket. Something like $s3->modifyBucketPermissions($bucket, $permission) where $permission is public, public-read, or private. It might be useful to have a function to obtain the current permission settings for a given bucket as well. It’ll be far faster to wait for you to add this than to move the data elsewhere, recreate the bucket, and move the data back…

    Here’s the documentation link for it:

    http://docs.amazonwebservices.com/AmazonS3/2006-03-01/RESTAccessPolicy.html

    -- Heron on 24 June 2008

  79. @Heron: You can do this:

    $acp = $s3->getAccessControlPolicy($bucketName);

    // Here you would modify $acp

    // Then update it
    $s3->setAccessControlPolicy($bucketName, ‘’, $acp);

    -- Don on 25 June 2008

  80. as has been said many times this is great. I have a very simple goal and am struggling to make it work. I want to get a secure file that is on my S3 and display it (gif, jpg, mov, whatever). I have successfully accessed the file but how do I display it now? I don’t need to actually DL the file just want to access it. Maybe this class is not good for that?

    -- scott t on 25 June 2008

  81. @scott: You probably don’t want to use the S3 class if you’re just trying to display the image on a webpage. Just link to it on the web page you want to display it on:

    <img src=“http://bucket-name.s3.amazonaws.com/imagename.jpg”/>

    Perhaps what you’re trying to do is this:

    Using the S3 class, grab a list of the contents of the bucket. Then, spit out an image tag (or a movie tag or whatever) for each item in the bucket, or for one particular item, etc. You don’t need to download the file using the S3 class though, just point a tag at it.

    -- Heron on 26 June 2008

  82. I keep getting this error for files over a certain limit. A file of 14KB is fine but a file of 140KB fails.

    Warning: S3::putObject(): [505] Unexpected HTTP status in C:\Inetpub\wwwroot\s3\S3.php on line 325

    -- Cameron on 5 July 2008

  83. Hi,

    This is great stuff. But can I use this library to list the contents of a particular bucket using a KEY? Like list all files in a buket “SWAP” which start with “SWAPNIL-” ?

    I see you have a listBuckets() function which will list all buckets, but not the contents of each?

    Please let me know as soon as you can.

    Thanks,
    Swapnil

    -- Swapnil on 6 July 2008

  84. @Cameron:

    Are you uploading the file to your web server and then uploading it from there to S3, or are you uploading the file directly from your computer to S3?

    @Swapnil:

    $contents = $s3->getBucket($bucketName);

    That will put the contents of $bucketName in $contents. You can then loop through $contents yourself and filter out the ones you don’t want. Read through example.php if you need to see how to use getBucket().

    -- Heron on 6 July 2008

  85. Hey, this is awesome.

    Just wondering, is there a way to supress the error when a particular file/key doesn’t exist?

    I’m trying to use this class to check if a particular file exists and if so, print out HTML code to display the thumbnail and link to the larger version.

    Any ideas/suggestions?

    Great script! B

    -- Ben on 7 July 2008

  86. @Ben: You might try the getObjectInfo() function. Pass it the bucket and uri; if it returns false, the object doesn’t exist.

    E.g.:
    if($s3->getObjectInfo($bucket, $objectname, false))
    { echo ‘anchor tag here’; }
    else
    { echo ‘no such file’; }

    The third parameter is important; you’ll get a bunch of object info instead of true/false if you don’t pass false for the third parameter.

    -- Heron on 8 July 2008

  87. Hello,
    Delete operation doesn’t work whith files containing a space character.

    -- johan on 9 July 2008

  88. @johan: I ran into that same problem. You need to urlencode the object name in the delete request. For example:

    $s3->deleteObject($bucketname, urlencode($objectname));

    -- Heron on 9 July 2008

  89. Same problem as Cameron #82. I used “S3 Firefox Organizer” to upload a bunch of test files to S3. I can use $s3->getBucket in a test script to obtain a list of the files and related data, all OK. But use of $s3->getObjectInfo on the same bucket and a specific object fails with “505 Unexpected HTTP status” error. The line in S3.php named is in the method code.

    Thank you for this excellent module and I appreciate any help.

    Chuck

    -- Chuck on 10 July 2008

  90. @Chuck: What is the name of the object you’re trying to get info on? Does it have a space or other symbols in the name? If so you might try using urlencode() on the object name before passing it to getObjectInfo().

    -- Heron on 10 July 2008

  91. Ah, you are the man! I used your idea to urlencode the filename and my test code now works.

    Thank you very much for the prompt and effective response.

    Chuck

    -- Chuck on 10 July 2008

  92. I’ve found the need to add some code to add Cache-Control header for my puts to S3. I think others would find this functionality useful as well. For my usage, I set the Cache-control so static files are not repeatedly downloaded from S3. Please let me know if/how you would like my code addition (e.g. diff, zip of s3.php or just the function).

    Thanks for a great library!

    -- Angelo Mandato on 12 July 2008

  93. @Angelo: I’ve added some changes for request headers. Thanks for providing the info.

    -- Don on 12 July 2008

  94. I added the delimiter field in for getBucket():
    <code>
    public static function getBucket($bucket, $prefix = null, $marker = null, $maxKeys = null, $delimiter = ‘/’)

    {

    $rest = new S3Request(‘GET’, $bucket, ‘’);

    if ($prefix !== null && $prefix !== ‘’) $rest->setParameter(‘prefix’, $prefix);

    if ($marker !== null && $marker !== ‘’) $rest->setParameter(‘marker’, $marker);

    if ($maxKeys !== null && $maxKeys !== ‘’) $rest->setParameter(‘max-keys’, $maxKeys);

    if ($delimiter !== null && $delimiter !== ‘’) $rest->setParameter(‘delimiter’, $delimiter);


    </code>

    -- Christo on 13 July 2008

  95. Just checked out the latest version (have never used this class before) and I’m getting an error of curl_init not defined. Any ideas?

    -- Jed Wood on 14 July 2008

  96. @Jed: You need the CURL extension.

    -- Don on 14 July 2008

  97. Looks like it does not work with file with space in its name.
    It says:
    Warning: S3::copyObject(mybucket1, blz21 – 22_t.jpg, mybucket2, blz21 – 22_t.jpg): [505] Unexpected HTTP status in /home/username/S3.php on line 446

    Is it a bug or i have to encode the file name ?

    -- Memphis on 18 July 2008

  98. If you’d read previous posts, Memphis, you’d know the solution: You need to urlencode the object name before sending it.

    -- Heron on 19 July 2008

  99. Thanks, it works well. Do you know how to use getBucket() to list a part of objects in a bucket. For example I have 10000+ objects in my bucket and I want to list the first 1000 and then the next 1000 objects … in alphabetical order ? The $maxKeys param does not work for me in that case

    -- Memphis on 19 July 2008

  100. Memphis: If I remember right, it gives you the files from A-Z, 0-9, then a-z in that order. That’s not exactly alphabetical; it’s ordered by ASCII code. If you want it to be in case-insensitive alphabetic order, you’ll need to grab all the keys yourself, sort them, then cache them locally before displaying them to the user.

    -- Heron on 20 July 2008

  101. Is there any chance to add curl_multi functionality so that we can send requests in parallel?

    -- Mike on 21 July 2008

  102. @Mike: You could.. But unfortunately that functionality will not be added anytime soon.

    -- Don on 21 July 2008

  103. getObject method seems to be capable of writing object to a resource. but how can I make the process in which server side php requests S3 object with authentication and client user can download the object from browser?

    -- Randy on 23 July 2008

  104. Randy: I assume you’re asking because you’ve made your files private. You can generate a temporary download link using this method:

    http://docs.amazonwebservices.com/AmazonS3/2006-03-01/index.html?RESTAuthentication.html

    Scroll down to the part labelled “Query String Request Authentication Alternative” near the bottom. Using this method, you can give your users a controlled way to download private objects from your buckets.

    This thread goes in to more detail: http://developer.amazonwebservices.com/connect/thread.jspa?messageID=71402&#71402

    -- Heron on 25 July 2008

  105. Don, you forgot to update the version number in S3.php on SVN.

    -- Heron on 28 July 2008

  106. @Heron: Haha, I did, didn’t think anyone would notice :-)

    -- Don on 28 July 2008

  107. Hi, only small update to your S3.php class (in function inputFile), you can use
    [ — cut — ]
    if ( !is_readable($file) || !is_file($file) ) {
    [ — cut — ]

    instead of

    [ — cut — ]
    if (!file_exists($file) || !is_file($file) || !is_readable($file)) {
    [ — cut — ]
    “is_readable” will check if file really exists.
    Cheers!

    -- profes on 1 August 2008

  108. Don,

    I’m having trouble getting bucket logging to work. I have a private bucket “foobar.company.com” and another private bucket “company-access-logs”. It is my understanding that this:

    $s3->setBucketLogging(‘foobar.company.com’, ‘company-access-logs’, ‘foobar-’);

    should enable logging for bucket foobar.company.com into bucket company-access-logs. However, I get the following error:

    Warning: S3::setBucketLogging(foobar.company.com, ): [InvalidTargetBucketForLogging] You must give the log-delivery group WRITE and READ_ACP permissions to the target bucket in /var/www/localhost/htdocs/s3/S3.php on line 491

    Any idea what I’m doing wrong?

    -- Heron on 4 August 2008

  109. Solved via e-mail. Thanks, Don :)

    -- Heron on 4 August 2008

  110. Hi Don,
    I modified your copyObject slightly to make a new setObjectInfo that allows for replacing meta data on objects without copying using x-amz-metadata-directive. This was very useful for me to fix Cache-Control headers on lots of images already uploaded. I’ve used it and it appears to work but one thing I noticed is that a new Content-Type needs to be supplied or else browsers may not respond as expected. I also added an acl parameter so that could be changed too. I added the code here but it doesn’t show well at all. Please email me and I’ll give it to you as it may be handy for others too. It was also nice for removing junk headers added by s3fox – didn’t like that stuff showing up anyway.

    -- Chris Savery on 7 August 2008

  111. A little help on creating folders.

    Just add the folder name to the file name (not the temp file name).

    So it would be like:
    $s3->putObjectFile($fileTemp, $bucketName, ‘images/’.$fileName, S3::ACL_PUBLIC_READ, array(),‘image/jpg’);

    Remember to add the contentType at the end if you want to be able to view your files via the browser.

    -G

    -- Giraldo on 8 August 2008

  112. I had another “SignatureDoesNotMatch” issue this evening, might have been the same one affecting others. Some file uploads would work, some wouldn’t.

    Turns out mime_content_type() was returning an extra tab character at the end of the string, which was throwing of the signature.

    trim(mime_content_type($file)) on line 707 fixed the problem.

    Thanks for a great bit of code!

    -- David Kennedy on 13 August 2008

  113. @David Kennedy: Thanks for the heads up!

    -- Don on 13 August 2008

  114. hi
    i have some confusion with listBuckets() function
    bcaz it shows the list of bucket which you had created but i have to validate new bucket name by matching all over the bucket names so how can i do that??
    is there any reference or function for the same???

    thanks in Advance

    -- mahendra on 26 August 2008

  115. Are you asking how to tell if a given bucket name is taken globally? You can’t, except by trying to register it. The best you can do is test it against the list of buckets you have registered yourself – that is, the ones returned by listBuckets.

    -- Heron on 27 August 2008

  116. hi Heron
    Thanks 4 replying..
    but actually when i use to create the bucket using s3 class as fuction putBucket ($bucketname) so what it does it just over right that bucket and updates the time of creation and no error received that bucket already exists or any error code

    that’s why i am confused that why it is doing so either i am doing something wrong… or pl suggest me…

    -- mahendra on 28 August 2008

  117. Hey bro :)

    Blog is coming along nicely. Loving the tutorials! About to code something up for my GuitarForum.co.za peeps using the S3 class. Thanks!

    -- Norio on 31 August 2008

  118. Your S3 PHP Class rocks!! Love that it does not require anything more than php 5.2. and I got my work done entotale in about 5hrs today. That time does include tweaks for human readable output. THANKS!

    -- mmoney on 2 September 2008

  119. @Don

    Hey, nice script! Saved me there from alot of work. However I cant seem to find a solution to increase Filesize. It returns to page.php every time I try to upload a file. It loads for while but then goes back to the upload page. It occurs when I try to upload files greater than 10mb from HD to s3

    -- Jones King on 2 September 2008

  120. Jones King,

    Is your webserver set to time out if the php script runs longer than some amount of time? You’ll probably want to check that…

    - Heron

    -- Heron on 3 September 2008

  121. I’m currently trying to figure out the best way to initially upload the file to my server before using your class to send it over to my S3 bucket. The files are quite large (maximum 250MB) and a standard HTML POST form seems patch at best.

    I’m using putObjectFile and passing it $_FILES[‘file’][‘tmp_name’]. I’ve heard that PHP loads the entire file into memory, or is that only if move_uploaded_file is called, am I mistaken entirely?

    -- Nial Giacomelli on 3 September 2008

  122. Nial,

    I’m not sure what PHP’s behavior is; however paying for bandwidth twice (uploading to your server, then uploading to Amazon) seems superfluous. To help with this, Amazon provides a method to POST a file directly to your bucket, given your authorization, and then redirect to a page of your choice. You can find more information here:

    http://developer.amazonwebservices.com/connect/entry.jspa?externalID=1434&ref=featured

    Enjoy :)

    -- Heron on 5 September 2008

  123. @Nial: That’s a very good question… You are probably correct though. I would assume that the entire file would be loaded into memory on POST.

    -- Don on 5 September 2008

  124. Just wanted to say thanks for a great script! I’ve been using it to upload files from my webhost to amazon s3, but on some files I get this error:

    Warning: S3::putObject(): [0] Missing input parameters in /directory/containing/S3.php on line 339

    Anybody else have this problem? (I am using the 7/30/08 version)

    -- Xenny on 6 September 2008

  125. there’s a bug where sprintf blows up if there’s a % in the variable value in your sprintf calls. use %s placeholders everywhere and move the variables into the argument list

    -- jasonmog on 8 September 2008

  126. I have a lot of data to load into S3 so I did some benchmarking. I PUT 1000 small objects into a bucket with both classes:

    CURL version took 7.5 minutes.

    PEAR version took 4.5 minutes.

    Of the 7.5 minutes, 6.5 are spent inside putObject’s $rest->getResponse() call.

    I hope this will encourage you to try and optimize your class. I still plan to use it in production either way though so thanks for making it :-)

    -- Brian on 10 September 2008

  127. @Malone, @Don Regarding empty files, there is a bug in the S3.php code.

    There are two conditional statements in S3::putObject that include $input[‘size’] > 0. If you replace those with $input[‘size’] > -1 it works fine.

    I’m not sure why the check is there in the first place, but anyway this fix will still prevent negative sizes.

    Cheers!

    -- Chris Snyder on 10 September 2008

  128. Found another bug, in that CURL is not verifying the SSL certificate coming from s3.amazonaws.com. If we care enough to use SSL, we should definitely be checking the certificate. ;-)

    Anyway, issues have been added at code.google.com for this and comment #127.

    Thanks for such a useful class!

    -- Chris Snyder on 10 September 2008

  129. @Chris: Regarding SSL verification: it is not a bug – CURL will spit out warnings otherwise.

    -- Don on 11 September 2008

  130. @Chris: The 0 byte check exists because I never expected people to upload empty files :) Will change in the next update anyway.

    -- Don on 11 September 2008

  131. @Brian: Sorry to say it but this isn’t a meaningful benchmark. Firstly, I cannot see how PHP code could be faster than C code. Secondly, the PEAR version buffers files in memory and using CURL we are actually able to have optimal unbuffered read/writes.

    -- Don on 11 September 2008

  132. Don – Brilliant class, implemented in no time at all!

    Are you planning to add support for folders within buckets?

    -- David on 11 September 2008

  133. Hi Don, first of all, great work. I’m using your class to back up my database to S3. Quick question, what are the advantages or disadvantages of your class versus a PEAR based class? The class offered by neurofuzzy.net uses two PEAR extensions (HTTP_Request and HMAC). Is there a speed difference? Which class is better at buffering and streaming large files? Thanks for your help. Keep up the good work.

    -- Kevin on 11 September 2008

  134. @Kevin: There are a number of advantages – but the fact that there are no PEAR dependencies is usually enough for most. And if you are copying large files, this is the one you want to use.

    -- Don on 11 September 2008

  135. @David: There is a way to emulate folders by using “_$folder$” – but you would have to look into it… If you create a folder with S3Fox and retrieve a list using this class you will see how it is done.

    -- Don on 11 September 2008

  136. Hi Don, one more question. Are we still limited to 2GB files max? Thanks.

    -- Kevin on 11 September 2008

  137. @Kevin: Yes. See http://bugs.php.net/bug.php?id=27792 for more information.

    -- Don on 11 September 2008

  138. Don, when we add a delimiter to getBucket(), do you roll up the prefix? I notice the results still have all the prefixes attached to the front of the key names. Thanks.

    -- Kevin on 13 September 2008

  139. Hmm, this is not working for me. Tried it in various hosting environments and I’m still experiencing the issues outlined in comment #14:

    “When I try to upload large files it will just stop after a couple minutes. It gives no error.”

    Any ideas?

    -- Sven on 14 September 2008

  140. @Sven I’m having the same problem but only on large files. I tried setting CURLOPT_TIMEOUT to a high number to no avail.

    -- Bryan on 17 September 2008

  141. @Sven and Bryan: If you’re using this class to upload from a user’s computer to an S3 bucket, you may want to consider POSTing directly to the S3 bucket, as explained in this article:

    http://developer.amazonwebservices.com/connect/entry.jspa?externalID=1434&ref=featured

    If, however, you’re using it to upload files from your server to S3, you’ll need to figure out what your Apache settings are and tweak them so Apache doesn’t time out.

    -- Heron on 17 September 2008

  142. @141 – The following PHP specific tutorial is good, and uses AJAX to upload files

    http://developer.amazonwebservices.com/connect/entry.jspa?externalID=1618

    -- David on 23 September 2008

  143. I have a question similiar to Post 103. I have a file that is private and needs to be automatically downloaded. How is this be done with the getObject? Or is the best way to use “Query String Request Authentication Alternative?” Thanks.

    -- Sam on 25 September 2008

  144. Sam: You need the user’s browser to be able to download it? You’ll want to generate a temporary URL giving permission to download the private file, as in this article:

    http://developer.amazonwebservices.com/connect/thread.jspa?messageID=71402&#71402

    -- Heron on 25 September 2008

  145. I’ve got following problem – sometimes (10% probability?) when uploading to S3, my php (CLI, 5.2.0-8+etch11, libcurl/7.15.5 OpenSSL/0.9.8c zlib/1.2.3 libidn/0.6.5) hangs there forever eating 100% cpu. Strace shows only those two lines again and again…

    poll([{fd=4, events=POLLOUT, revents=POLLHUP}], 1, 0) = 1
    poll([{fd=4, events=POLLOUT, revents=POLLHUP}], 1, 1000) = 1

    It happens in putObjectFile() uploading cca 750MB file.

    Any idea what the problem might be?

    -- marek on 26 September 2008

  146. @Heron: Thanks for your help.

    -- Sam on 26 September 2008

  147. Your class didn’t allow for authenticating via query string:

    function getAuthenticatedUrl ($bucket, $resource, $expires_in) { $expires = time() + $expires_in; $string_to_sign = “GET\n\n\n{$expires}\n/{$bucket}/{$resource}”; $signature = urlencode(base64_encode((hash_hmac(“sha1”, utf8_encode($string_to_sign), self::$__secretKey, TRUE))));

    $authentication_params = “AWSAccessKeyId=” . self::$__accessKey; $authentication_params.= “&Expires=$expires”; $authentication_params.= “&Signature=$signature”;

    $link = “http://$bucket.s3.amazonaws.com/$resource?$authentication_params”;

    return $link; //url encode this? //echo “<a href=\”“.htmlentities($link).”\”>Authenticated Link</a>”;

    }

    -- pixelterra on 3 October 2008

  148. Don,
    I am using copyObject() function for copying various images within a bucket. It creates a copy of the image, but when i try to view the image, it says “Access Denied”…Could you figure out the reason.
    Many thanks.

    -- Pushpesh on 8 October 2008

  149. Don,
    In reply to my earlier query (no. 148), i have found a workaround for the stated problem. All i needed to do was to add the following :-
    $rest->setAmzHeader(‘x-amz-acl’, self::ACL_PUBLIC_READ);
    after the line
    $rest->setAmzHeader(‘x-amz-copy-source’, sprintf(’/%s/%s’, $srcBucket, $srcUri));
    in the copyObject(…) function.
    Thanks

    -- Pushpesh on 8 October 2008

  150. @pixelterra: Added getAuthenticatedURL()

    @Pushpesh: Added an ACL parameter to copyObject()

    -- Don on 8 October 2008

  151. great stuff, worked fine for me! have only done some simple uploading and deleting of objects, but I don’t really need more than that.

    -- marc breuer on 22 October 2008

  152. I’m not sure what causes this, but when I tried the script below:
    <?php
    include(‘S3.php’);
    if (!defined(‘awsAccessKey’)) define(‘awsAccessKey’, ‘MyAccessKey’);
    if (!defined(‘awsSecretKey’)) define(‘awsSecretKey’, ‘MySecretKey’);

    $s3 = new S3(awsAccessKey, awsSecretKey);
    $a = $s3->listBuckets();
    echo “S3::listBuckets(): “.$a[ 0 ].”\n”;

    define(‘BUCKET’,$a[ 0 ]);
    $RESPONSE = $s3->getBucket(BUCKET);
    ?>

    it outputs as:
    S3::listBuckets(): [My_Correct_Bucket_Name]
    Warning: S3::getBucket(): [NoSuchBucket] The specified bucket does not exist in …./s3/S3.php on line 134

    Something with my script, or with my S3 account? Thanks

    *I have to add spaces in $a[ 0 ] to avoid autolink

    -- Mike on 4 November 2008

  153. Mike: You’ll want to make sure there are no extraneous newlines or anything in $a[ 0 ]. Also, does it work if you do $s3->getBucket(‘putbucketnamehere’); manually?

    -- Heron on 5 November 2008

  154. Dear Don Schonknecht,

    Congratulations for S3.php file. Great job!
    However this class doesn’t allow list of objects based on queries, because you have an error on your S3Request class, but I already fixed it.
    Basically you weren’t parsing the uri’s “querystring” on the “StringToSign” (the “resource” variable) on the S3Request class.
    If you check the AWS-S3 tutorial, it says when you have a querystring on the uri, the StringToSign only should contains the bucket and the request_uri. This means you should only append, to the StringToSign, the path part of the un-decoded HTTP Request-URI, up-to but not including the query string.
    For more information see the 3rd example of the http://docs.amazonwebservices.com/AmazonS3/2006-03-01/index.html?RESTAuthentication.html url.

    Like I told you before, I already fixed this bug.
    Basically I added/changed the following lines on the S3Request constructor function:

    $resource_uri = strpos($this->uri, “?”) > 0 ? substr($this->uri, 0, strpos($this->uri, “?”)): $this->uri; if ($this->bucket !== ‘’) { $this->resource = ‘/’.$this->bucket.$resource_uri; $this->headers[‘Host’] = $this->bucket.’.s3.amazonaws.com’; } else { $this->headers[‘Host’] = ‘s3.amazonaws.com’; $this->resource = strlen($this->uri) > 1 ? ‘/’.$this->bucket.$resource_uri : $resource_uri; }

    This means the S3Request constructor has now the following code:

    function __construct($verb, $bucket = ‘’, $uri = ‘’) { $this->verb = $verb; $this->bucket = strtolower($bucket); $this->uri = $uri !== ‘’ ? ‘/’.$uri : ‘/’; $resource_uri = strpos($this->uri, “?”) > 0 ? substr($this->uri, 0, strpos($this->uri, “?”)): $this->uri; if ($this->bucket !== ‘’) { $this->resource = ‘/’.$this->bucket.$resource_uri; $this->headers[‘Host’] = $this->bucket.’.s3.amazonaws.com’; } else { $this->headers[‘Host’] = ‘s3.amazonaws.com’; $this->resource = strlen($this->uri) > 1 ? ‘/’.$this->bucket.$resource_uri : $resource_uri; } $this->headers[‘Date’] = gmdate(‘D, d M Y H:i:s T’); $this->response = new STDClass; $this->response->error = false; }

    If you have any other question, don’t hesitate to email me to a19836@gmail.com.

    Sincerely,
    Joao Pinto

    -- Joao Pinto on 6 November 2008

  155. Dear S3 Owners,

    I’m changing the S3 class functions, to return a list of objects and a list of Objects Info, if the uri contains a query string.
    I will send you the new code to your email or post it here, when will be ready.
    Can you send me your email to a19836@gmail.com, please?

    Regards,
    Joao Pinto

    -- Joao Pinto on 6 November 2008

  156. Any chance you might support Cloudfront in the future?

    -- Kevin on 19 November 2008

  157. Setting metadata headers isn’t quite working… I’m using…

    $metaHeaders[‘Content-Type’]=‘image/png’;

    …and then including $metaHeaders in the putObject instruction.

    I’m getting back:

    x-amz-meta-content-type: image/png
    Content-Type: application/octet-stream

    What am I doing wrong?

    -- James Cridland on 20 November 2008

  158. @Kevin: Written some code for CloudFront – it will most likely appear in the next release.

    @James Cridland: The $metaHeaders are the x-amz-meta-* headers. What you need to do is use $requestHeaders[‘Content-Type’] = ‘image/png’

    -- Don on 20 November 2008

  159. Closer, but still no dice. Now using putObjectFile – Content-Type works fine (as a part of the call), but

    $requestHeaders[‘Cache-Control’]=‘max-age=315360000’;

    … doesn’t really do the job, returning

    x-amz-meta-cache-control: max-age=315360000

    I could really do with setting the cache-control properly…

    -- James Cridland on 21 November 2008

  160. OK, worked a workaround.
    $requestHeaders[‘Cache-Control’]=‘max-age=315360000’;
    $requestHeaders[‘Content-Type’]=‘image/png’;

    $transfer->putObject($transfer->inputResource(fopen($file, ‘rb’), filesize($file)), $bucketname, $object_name, S3::ACL_PUBLIC_READ, array(), $requestHeaders)

    That works, though not using putObjectFile. But it works. So. Good. ;)

    (That sets a ten-year cache on that image, btw.)

    -- James Cridland on 21 November 2008

  161. Hey, CloudFront support already, nice! Didn’t there used to be a PayPal link around here somewhere?
    Well anyway, thanks for keeping up with the latest and continuing to improve the class.

    -- Chris Snyder on 27 November 2008

  162. Question about licensing- I’ve create a Drupal module that uses S3 as a storage option (http://drupal.org/project/media_mover) and currently uses a different S3 library which does not use curl. I’m interested in using your library, but to include it in Drupal’s CVS repository, it would have to be under the GPL. Would you consider releasing it under the GPL as well?

    Thanks so much

    -- arthur on 2 December 2008

  163. Arthur, this S3 class is released under something like the BSD license (which is more permissive than the GPL). I’m not a lawyer but I don’t think there’s a problem with using the S3 class in conjunction with GPL’ed code, as long as you retain the license information given in the S3.php file and maintain a clear separation between your code and the S3 class.

    -- Heron on 3 December 2008

  164. Heron- unfortunately, the rules for the Drupal CVS repository are strict as far as I know- GPL or include a link to a location where the user can download the file. My preferences is obviously to include the class in the repository which is why I’m asking. I’m not a lawyer either, but I do know that every time the MIT/BSD/GPL thing comes up people seem to loose their minds which is why I’m trying to ask nicely :)

    -- arthur on 5 December 2008

  165. Have any of you tried changing the access control policy of objects in S3? I’m trying to add AllUsers READ access but it doesn’t seem to be saving the changes properly.

    First I use getAccessControlPolicy on the object to get $acp, then I add:

    $acp[“acl”][] = array(“type” => “Group”, “uri” => “http://acs.amazonaws.com/groups/global/AllUsers”, “permission” => “READ”);

    Then I set it back using setAccessControlPolicy but it doesn’t seem to be storing the change.

    Thanks

    -- Kevin on 5 December 2008

  166. Oops I apologize. I figured out the problem. Sorry, I wish I could delete the last comment. Thanks again for your work Don!

    -- Kevin on 5 December 2008

  167. Thanks a lot for this class ! It’s clean and very helpfull :)

    -- Mat on 17 December 2008

  168. Nice class. Just wanted to let everyone know about a problem I ran into using this. I was getting [NotSignedUp] Your account is not signed up for the S3 service. You must sign up before you can use S3.
    I am running on a Windows machine and it is apparently an issue with the SSL cert on Amazon and my windows box. I’ve turned off SSL for now and will go back and investigate when I get everything else working. Just didn’t want anyone else to bang their head against a wall on this one.

    -- david on 17 December 2008

  169. exapmle.php is not working.. showing the following error message.

    Warning: S3::listBuckets(): [6] Could not resolve host: s3.amazonaws.com; No data record of requested type in E:\softwares\xampp\htdocs\s3-php5-curl\S3.php on line 89
    S3::listBuckets():
    Warning: S3::putBucket(s3test4949e15c40dfb, public-read, ): [6] Could not resolve host: s3test4949e15c40dfb.s3.amazonaws.com; No data record of requested type in E:\softwares\xampp\htdocs\s3-php5-curl\S3.php on line 210
    S3::putBucket(): Unable to create bucket (it may already exist and/or be owned by someone else)

    -- shibu on 18 December 2008

  170. Great class, very well written.

    It would be nice if it was possible to get the CommonPrefixes out when doing getBucket() with a delimiter

    -- Sune Kibsgaard Pedersen on 23 December 2008

  171. Hey guys,

    I created a new Google project with a file manager to the Amazon S3 service.
    Please visit the following page: http://code.google.com/p/awss3filemanager/.

    I’m searching coders to help me improving this amazon file manager project.

    Sincerely,
    Joao Pinto

    -- Joao Pinto on 23 December 2008

  172. Maybe I am miss-using the function, but for putObject(), if you pass an empty value or false for $metaHeaders there is no error checking so on line # 349 you get an error when it tries to run a foreach on $metaHeaders.

    Is this by design, am I supposed to pass a different (empty) value when I don’t have any metaHeaders but do have requestHeaders?

    Example:
    $s3->putObjectFile($filePath, $bucketName, $fileName, S3::ACL_PUBLIC_READ, ‘’, $requestHeadersArray);

    Using version 0.3.9

    BTW, thank you very much for creating this class, it’s been very useful!

    -- sam on 29 December 2008

  173. Sam,

    If you pass array() as that parameter, you should be fine. For example:

    $s3->putObjectFile($filePath, $bucketName, $fileName, S3::ACL_PUBLIC_READ, array(), $requestHeadersArray);

    That way foreach will accept the parameter as valid but will do nothing since the array is empty.

    -- Heron on 30 December 2008

  174. Wow S3 isn’t too frustrating… I appreciate you making this class. I’m very surprised Amazon didn’t make this already though.

    -- Lint Filter on 31 December 2008

  175. Lint Filter,

    Amazon made the REST API (and the SOAP API), and they also provided examples of those APIs being used in various languages. Trouble is, their example implementations are fairly basic and not particularly general-purpose. That’s where Don’s S3 class comes in :)

    -- Heron on 31 December 2008

  176. Hey guys,

    I found out a bug on the S3 class,in the putObjectString function.
    Basically if you try to create an object through the putObjectString function with a numeric input, the S3 class returns a php error.
    So the putObjectString function should resolve these kind of issues.
    I propose the following:

    public static function putObjectString($string, $bucket, $uri, $acl = self::ACL_PRIVATE, $metaHeaders = array(), $contentType = ‘text/plain’) { if(!is_string($string)) { if (is_object($string) && in_array(”__toString”, get_class_methods($string))) $string = strval($string->__toString()); else $string = strval($string); } return self::putObject($string, $bucket, $uri, $acl, $metaHeaders, $contentType);
    }

    -- Joao Pinto on 3 January 2009

  177. Hey guys. I’ve got a problem. I read in other comments about making the file private and wanting to control access to the file when downloading. I don’t want to do the temp url thing, I’m not sure if that is secure enough (maybe it is, if so I’d like to know why ;) ) but instead, I’d like to use the getObject method to grab the file information and put that into somesort of php file object and then do the file output where you create the File headers, then do a “readfile($filename);” and it send it to the browser.

    I could potentially just save the file down to the hard disk, and server it up, then delete it once it’s been downloaded, but that may prove inefficient. I’d like to just have it load in memory, never touch the hard disk, and load right into a php file object and then pump that out to the page (espically for images etc.)

    Any thoughts? I’d like to use the direct link to the file on amazon, i just want to make sure i can control access to the file.

    Thanks in advance!

    -- Travis Cooper on 6 January 2009

  178. Travis,

    A generated temporary URL is about as secure as it gets. You set the expiration time; I would suggest setting an expiration two or three minutes in the future. Yes, the URL works for anyone, but only during that two minute window, and if your user wants to share the file they could just do it after downloading it anyway.

    The best option is to control whether or not you provide the signed URL in the first place; for example, I put the full version of my software in a bucket, and provide a signed temporary download URL for the user when they’re logged in to my website. That is, they only get the URL once I’ve already verified who they are, and even then the download URL is only valid for a few minutes. I can’t stop them from throwing the program up on BitTorrent or something, but that’s an impossible dream anyway.

    I’d recommend against downloading the file to your server and then serving it to the user; you lose one of the best benefits of using S3 (which is that Amazon’s pipe is bigger than yours).

    -- Heron on 8 January 2009

  179. No worky.

    I hate the fact that I am posting here with problems but I just can’t make it work.

    I get “Warning: Invalid argument supplied for foreach() in /srv/www/htdocs/upload/page.php on line 49″

    Which isn’t a big deal. The main problem is that whenever I try to upload an image (tried many file types including jpg, jpeg, gif, etc) I get “Something went wrong while uploading your file… sorry.” With no direction as to where I went wrong.

    I tried creating a new bucket by hand and using it, I even tried letting it create one. It’s not the bucket name cause as soon as it fails I can go and create one with the same name. There is no real error reporting with this class to help me figure it out so I have to post here.

    Any help/guidance would be greatly appreciated.

    -- Jason on 12 January 2009

  180. Jason,

    If you e-mail me your code for page.php (be sure to remove your AWS keys!) I may be able to help you figure out what’s going on.

    heron@xnapid.com

    - Heron

    -- Heron on 16 January 2009

  181. Used the library for the first time today. Really easy to use. I have two questions/comments

    a) I assume it the caller’s reponsibility to retry if an InternalError is returned.

    b) I did a putBucket on the same bucket multiple times today. They all indicated success. Should’nt I be getting a BucketAlreadyOwnedByYou ?

    Thanks

    -- sharad on 25 January 2009

  182. Anyone know what might cause this?:

    PHP Warning: S3::putObject(): [MalformedXML] The XML you provided was not well-formed or did not validate against our published schema in includes/S3.php on line 357

    I can list buckets ok, but putObject with a string for the input parameter generates that (and doesn’t work).

    Thanks.

    -- David J on 26 January 2009

  183. @David J – to answer myself :)

    It occurs when the bucket is the empty string.

    -- David J on 27 January 2009

  184. I love this class, but I recently ran into an issue with a server that does not have any certificates installed. For situations like these, I’d like to see one or more of the suggested solutions added…

    1: allow the user to specify a define to turn off CURLOPT_SSL_VERIFYPEER. e.g.

    curl_setopt($curl, CURLOPT_SSL_VERIFYHOST, 1);
    if( defined(‘S3_CLASS_SSL_VERIFYPEER’) ) curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, S3_CLASS_SSL_VERIFYPEER);
    else curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, 1);

    Then all a developer needs to do is add the following line to their code before using the S3 class:
    define(‘S3_CLASS_SSL_VERIFYHOST’, 0);

    2 and 3: Another option(s) would be to allow us to define the path for CURLOPT_CAINFO and/or CURLOPT_CAPATH so we can specify the .crt files.

    Thanks again for a great S3 class!

    -- Angelo Mandato on 30 January 2009

  185. Is there any way to use this class but somehow ‘encrypt’ or ‘sign’ an access key so that if the code gets taken, I can disable the access key only for this script?

    The problem is that our site has multiple developers working on it and I really don’t want to just hand out the keys for everyone to have access to.

    -- R. Moose on 2 February 2009

  186. @ R. Moose:

    Unfortunately no, there is no way to do that. If the key gets taken, you’ll simply have to generate a new keypair from your AWS account.

    One solution, would be to create a web service with your own authentication mechanism that does the interaction with S3 itself.

    However, there is a larger problem here. If you don’t trust your developers with your access key, why do you trust them to write the code in the first place? Being able to revoke an access key won’t prevent them from (for example) deleting the contents of your buckets, sabotaging your code, etc etc. I’d recommend hiring trustworthy developers ;)

    Non-disclosure agreements can cover this type of thing, giving you legal grounds to sue them into oblivion to recover your costs if they use such secrets to damage your company. It also doesn’t hurt to change your access keys whenever a developer leaves the company.

    -- Heron on 2 February 2009

  187. Thanks for some great work — nice to use well-done code.

    -- Dave on 10 February 2009

  188. Great class!
    Congrats! :D

    I’m, sort of, disliking echoing ‘$s3->getAuthenticatedURL’ but that’s me.

    But talking about the expire time. Let’s say it’s set to 2 minutes. Does that mean if you download a file that would take 3 minutes to download, you will get an error after 2 minutes because of lacking access to s3?

    Cheers!

    -- Ricky de Jong on 10 February 2009

  189. Ricky:

    No, the download will continue as normal. The expiration time is for the start of the download (that is, for the moment when the link is clicked).

    -- Heron on 11 February 2009

  190. @Ricky: With regards to getAuthenticatedURL(), see what Heron said above.

    What you want to do is create a link with getAuthenticatedURL() as the user clicks, so: /download/fileId redirects to the getAuthenticatedURL() link. This way the link is always valid.

    -- Don on 11 February 2009

  191. Thanks for your quick reply, Don and Heron :)

    Greets

    -- Ricky de Jong on 11 February 2009

  192. @Heron

    Just a small note: the expiration time for the getAuthenticatedURL is when it’s been generated and not when you click on it :D

    -- Ricky de Jong on 11 February 2009

  193. Hi,

    Firstly, many thanks for the class – it’s just what I was looking for.

    Unfortunately I’m getting the same timeout errors mentioned by others in earlier posts, when calling putObjectFile. The timeout occurs trying to put a small file (less than 3k), and it’s the second putObjectFile on the same S3 object – the first call works perfectly. Any ideas?

    Thanks!

    -- Ben Garrett on 11 February 2009

  194. @Ricky:

    What I meant was, the expiration date determines the range of time during which the link may be clicked on. If the link is clicked outside of that time frame, it is invalid.

    So, the link may be clicked on (or visited in some other way) at any time between the moment it is generated and the expiration time.

    -- Heron on 11 February 2009

  195. First, awesome resource! Currently, I am creating a class which takes a bucket of files and matches it with its backup bucket. If the file is not there in the backup, then it copies and pastes it in the backup bucket. Just a simple backup program between two buckets in one S3 account.

    I am running into a problem. The getbucket function is not grabbing all of the files in the first bucket(we have a ton). Any idea of whats going on? Is it the PHP function’s fault? Is there a limit of how many files one can grab from a bucket?

    -- Kirby on 13 February 2009

  196. I’m getting the timeout too, but only on the second file upload. What I’m doing is:
    1.) putObjectFile to upload (/tmp/temp.jpg)
    2.) resize image to make thumbnail to the same file (/tmp/temp.jpg)
    3.) putObjectFile to upload thumbnail (/tmp/temp.jpg)

    If during the resize I create a new file (/tmp/temp2.jpg) then it works fine.

    -- Jonathan on 13 February 2009

  197. @Ben, @Jonathan: Think there is an issue with resources being locked/not cleaned up.

    @Kirby: Will look into it.

    -- Don on 13 February 2009

  198. Isn’t there a “paging” sort of thing going on when you list the contents of a bucket? That is, I was under the impression that listing the bucket contents can only happen a thousand objects at a time.

    -- Heron on 14 February 2009

  199. Love the code, however updated to 0.3.9 and it seems to still have issues with files of 0 bytes. I keep getting the Missing input parameters error. I see this was mentioned before in the comments, is this still in the todo’s?

    Thanks.

    -- Kevin on 16 February 2009

  200. Kevin,

    After fiddling with 0.3.9’s source for a while, I’m still finding that the only way to put a 0-byte file onto S3 is by POSTing directly to S3 (via an HTTP POST, that is), as described here:

    http://developer.amazonwebservices.com/connect/entry.jspa?externalID=1434&ref=featured

    It’s worth noting that it’s not just 0.3.9 that has this issue with 0-byte files; the S3Fox Firefox extension won’t let you upload 0-byte files either.

    -- Heron on 17 February 2009

  201. @Kevin: With regards to 0 byte files, you can get around it by setting $requestHeaders[‘Content-Length’] = 0; when using putObject().

    -- Don on 17 February 2009

  202. @Kirby: I had a look into this and could not reproduce your problem. I’m inclined to think your problem is related to a memory limit or perhaps even a bug in your PHP or libcurl versions.

    -- Don on 17 February 2009

  203. I wasn’t able to get the $requestHeaders[‘Content-Length’] = 0; to work, it then just gave me another error which I don’t have in front of me, something about the method has not been implemented or something. But I’m managing my files database locally so I’m able to adjust my logic to make everything work fine. Thanks for doing the S3 integration for me. :)

    -- Kevin on 17 February 2009

  204. Is there any known issues with ZendCore v2.5 install (PHP & Apache2) and OpenSSL 0.9.8i. The PHP info page says everything is installed but everything seems to fail unless I pass ‘false’ along with the S3 constructor. Other than that I have got this to work on other installs with out issue. Thanks.

    -- Lionel Morrison on 18 February 2009

  205. Lionel,

    The third parameter to the constructor is whether to use SSL when communicating with S3. If you get errors with S3 enabled, you might need to check to make sure you have updated certificates installed on the server.

    If you’re not sure, or they look like they’re installed, you don’t lose much by using a non-SSL connection; there isn’t any sensitive data in the request URL, so unless you’re storing sensitive data there isn’t a problem.

    If you are storing sensitive data, you should be encrypting it on your end before uploading anyway ;)

    -- Heron on 18 February 2009

  206. Great peace of work!
    Doeas anybody know how to retrive the total number of files in a bucket? I need this for pagination…

    -- Max on 12 March 2009

  207. @Max: The S3 class will grab all of the items in the bucket unless you specify the max number of items to return. There’s no way to get just the total number of items in the bucket, but you could cache the results of a single call to getBucket and paginate the results yourself (instead of using the class to do it).

    -- Heron on 13 March 2009

  208. Great class, very easy to use!! Thanks a lot!

    One question : Does anyone know how to delete a folder inside a bucket? I know how to delete the files inside the folder but I’m not able to delete the folder itself.

    -- popnoart on 25 March 2009

  209. @popnoart: Buckets can’t actually contain folders. Some S3 clients (like S3fox) pretend there are folders based on the prefix of object names, but there aren’t really folders in buckets.

    -- Heron on 26 March 2009

  210. This seems like a great class, but the documentation seems a bit lacking.

    1. In your example.php file you call putObjectFile() but in the description for putObjectFile() it calls it a “legacy function”. So I’m not sure if I should be using it or not.

    2. The class documentation ( http://undesigned.org.za/files/s3-class-documentation/index.html ) seems incomplete as it does have any descriptions telling me what the functions do.

    -- Gerry on 7 April 2009

  211. Gerry,

    putObjectFile() will always exist but for additional functionality (that was added much later on) you want to be using S3::putObject(S3::inputFile()) in its place.

    If you mouse over the method names on the documentation page there are descriptions available for each. But you’re right, the documentation definitely needs work – although I think there is enough information available since most folks don’t have trouble figuring things out :)

    -- Don on 7 April 2009

  212. Yes I saw the mouse overs. Ok well let me use putObject as an example, here is the definition:

    putObject (mixed $input, string $bucket, string $uri, [constant $acl = S3::ACL_PRIVATE], [array $metaHeaders = array()], [array $requestHeaders = array()])

    and the mouseover description is:
    “Put an Object”

    So the description didn’t add anything to what the name had already given me, I have no idea what the arguments are for or how they change what the function does. I understand how it’s hard to write descriptions for functions that you know inside out as everything seems obvious to the author, but I really have no clue as to what $metaHeaders is for. I have to look into the code and work out what everything in there does to figure it out, which kinda removes the point of having documentation.

    =========
    example ripped from the php manual:
    mixed str_replace ( mixed $search , mixed $replace , mixed $subject [, int &$count ] )

    This function returns a string or an array with all occurrences of [search] in [subject] replaced with the given [replace] value.
    =========

    Don’t get me wrong, I’m awful with my own documentation and in telling you all this I’m learning myself about how I should document things in future.

    -- Gerry on 7 April 2009

  213. Quick question.
    I’m working with some web services which automatically push content to my S3 bucket for me.

    The problem with this, is I’m not able to set the headers (eg. Expires) since their web service automatically pushes it on my behalf.

    Do you have any ideas on how best to retrieve an object, and set headers? I looked through your source, but didn’t notice any examples to specifically deal with an object that already exists, and changing it after it’s been put on S3.

    Thanks.

    -- onassar on 7 April 2009

  214. Figured out that the way to do this is to make a call via copyObject, stating the same source uri and bucket as the destinations.

    The exception here, being that you then must set the headers for the destination object.

    This class doesn’t have this, and I’ve tried hacking it but it wouldn’t seem to work.

    Any thoughts?

    -- onassar on 8 April 2009

  215. hi , we locate our media files at amazon s3.When a user request 4 download from Website (say x) , the file will be read at amazon & putted to X. How about directly downloading the file at users location.?

    -- parag on 9 April 2009

  216. This may be a silly question…are the calls this class makes secure by default or do I have to specify that somehow? I mean, can someone sniff the “secret access key” Amazon provides us or is it encrypted? Thanks, sorry to be a dummy, I am new to this! :)

    -- miguel on 16 April 2009

  217. Miguel,

    The secret key cannot be determined by an eavesdropper, whether or not you’re using an SSL connection. Don’t worry, it’s safe :)

    Look at the third parameter to the class constructor. By default, the class uses SSL to communicate with Amazon S3.

    -- Heron on 17 April 2009

  218. How to check expiry to the image?

    -- bhimrao on 22 April 2009

  219. Hi Don,
    First of all great work!
    I just wanted to let you know that when I try to use the putObjectString, the request hangs for 2 – 3 minutes and then returns true finally.

    This is the code I’m trying:

    //put body on S3 require_once(LIBRARY_PATH . “/s3/S3.php”); $s3 = new S3(AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY); if($s3->putObjectString(“hello loco”, ‘blovelspot’, ‘test/loco2.html’)) { print “success”; die; } else { print “fail”; die; }

    Any ideas what I’m doing wrong?

    -- Jorge Escobar on 5 May 2009

  220. How are you checking expires headers?

    -- bhimrao on 6 May 2009

  221. @bhimrao: To which headers are you referring?

    @Jorge: Can you duplicate the issue on another computer (preferably using a different ISP)? I haven’t seen anything similar.

    -- Heron on 7 May 2009

  222. i want to check the given expires header to the image
    that it is properly given or not

    -- bhimrao on 8 May 2009

  223. Hi Heron,
    I am doing this from an Amazon EC2 instance, so I’m sure it’s not related to the ISP. I did read in some Amazon forums there was some hanging on some file uploading operations when the files are not images but text (the objects I’m trying to upload are HTML files), so what I ended doing was saving the HTML object first on the /tmp directory of the instance and then doing a putObjectFile. That works perfectly, so I’m sticking with it.

    Thanks again for a wonderful library!

    -- Jorge Escobar on 12 May 2009

  224. What an excellent and simple implementation. First impressions are fantastic, not tried all the features.
    Many thanks for your contribution.

    -- Frank Unwin on 12 May 2009

  225. Jorge: I’m glad you’ve got it working. Come to think of it, I don’t think I’ve ever used putObjectString. I’ll pester Don into testing it sometime ;)

    -- Heron on 13 May 2009

  226. Great stuff but how do I just list files in a subfolder of bucket. e.g.
    $bucketName=“thebucket”;// but what i really need is thebucket/this/that/theother”;

    $contents = $s3->getBucket($bucketName);
    print_r($contents); // too much info – just want a sub listing!
    Thanks
    Kevin

    -- Kevin on 17 May 2009

  227. Kevin,

    S3 doesn’t support folders. Clients like S3Fox only imitate folders for convenience; as a result, this S3 class doesn’t support folders either.

    The best thing you can do, therefore, is use the $prefix parameter to getBuckets.

    For example:

    print_r($s3->getBucket($bucketName, ‘this/that/’));

    This would list the objects in $bucketname whose names start with ‘this/that/’ (which is how S3Fox emulates folders).

    -- Heron on 19 May 2009

  228. Hi there,
    thanks a lot for this usefull class!

    I’ve added a UTF8 check to the S3Request Class.
    That to ensure that specialchars such as umlauts are handled correctly.

    Here is how it works:
    I’ve added this check into the constructor of S3Request, right before the parameter $uri is assigned to the class var uri:
    <code>
    //Check if URI is not utf8 – encode it
    if(!$this->isUTF8($uri)){
    $uri = utf8_encode($uri);
    }
    </code>

    To do the Check I’ve used this snipped from http://ch.php.net/manual/en/function.utf8-encode.php#59710
    <code>
    /**

    * Checks if a given string is UTF8 encoded

    * @param string $string

    * @return boolean

    */
    protected function isUTF8($string){
    return ( strpos(utf8_encode($string),“ƒ”,0) !== false ); // “ƒ” is ALT+159, if at the beginning then it’s already utf8
    }
    </code>

    That’s all :)

    Cheers,
    shockfreezer

    -- shockfreezer on 22 May 2009

  229. Hello again,
    A little question, what’s the best way to move a file – I would rather not get and put. e.g. mv, unlink/link if it is possible. Many thanks for this code.
    Kevin

    -- Kevin Yeandel on 26 May 2009

  230. Amazing Amazon S3 Class!

    Is there a way to to use wild cards in deleteobject?
    i.e. Somthing like; DeleteObject($bucketName, ‘this/that/*.mp4’)

    Also is it possible to copy from a bucket in one account to bucket in a different account? (I own both accounts.)

    -- Steve on 31 May 2009

  231. Kevin: You cannot “move” an object; there is nowhere to move it from or to. A bucket is simply a collection of objects, an object is either in the bucket or it is not. You can copy it from a bucket to the same bucket with a different name, and then delete the old object, and that’s as close as you can get to renaming it.

    Steve: To your first question, no, you cannot use wildcards. S3 is not a file system and does not understand wildcards.

    You cannot directly copy from a bucket in one account to a bucket in another account using a single S3 service call; you’ll have to download it from the first account and upload it to the second account.

    -- Heron on 4 June 2009

  232. @Kevin – Thanks. My bad. Perhaps I didn’t ask the right question for my first question. How about Something like; DeleteObjects($bucketName,’/this/that)

    For example I upload 150 objects (from my PC)to S3 and catalog them in a database. Then to delete them I iterate through them thus;

    While($row = mysql_fetch_array($result))

    {

    $filename = $row[“FileName”] . “.mp4”;

    $S3File = S3Folder/$filename”;
    $s3->deleteObject($S3B,$S3File);

    }

    This takes 48 seconds. However do to the same thing from my PC using:

    “rescmd s3 delete-objects aws-key:awskey aws-secret:awssecretkey bucket:S3B key-prefix:S3Folder”

    takes only 8 seconds.

    I’m not sure what rescmd does in this case but I can’t think that first its requesting a listing of all objects in the bucket with the prefix then making all the individual delete operations 6 times faster than my php script. Does it?

    That’s too bad about not being able to copy to another account. What do people to for backup? In the hopefully unlikely case that my secret key is discovered it would be nice to have my stuff backed up in another account without the hassle of first downloading them to someplace else and then uploading again to S3. What a needless carbon footprint that produces. :-)

    -- Steve on 4 June 2009

  233. As far as backups go, you don’t need to worry about that; S3 handles redundancy and availability for you. Your data is stored on multiple physical drives in multiple geographically separated data centers (at least, that’s my understanding).

    As far as your secret key goes, well, keep it secret :) But if it does get discovered, you can just log in to your Amazon AWS account and get a new key (which erases your old one). No need to maintain (or pay for) a second AWS account.

    I’ll e-mail the S3 devs and ask them the best way to delete multiple files like that :)

    -- Heron on 4 June 2009

  234. The S3 API only supports deleting one file at a time. I suspect the reason using the S3 class takes so much longer than rescmd is that the S3 class re-creates an internal object and re-initiates a connection with curl for every call to deleteObject, where as (I suspect) rescmd does not.

    This isn’t really a bad thing, but it’s not optimal if you’re calling deleteObject two hundred times in a row.

    Maybe I’ll write a function to do batch deletes more efficiently later. I’m at work right now ;)

    -- Heron on 4 June 2009

  235. Hello, strange problem with getObjectInfo requests when SSL is enabled. This warning is triggered even when a prior putObject is successful:

    S3::getObjectInfo(bucket, file.png): [35] SSL connect error [S3.php: 443]”

    This means i cannot verify metadata so could you advise please? thanks

    -- Hash on 5 June 2009

  236. @Heron. Thanks for looking into optimizing deleteObject that would be great. The other issue about backup I understand that S3 has storage redundancy but that just protects the data thats there. If someone got the key and delete files then they are gone right?

    [slightly off topic mode on:]
    (This is why I’m worried about backup and why I’d like to be able to copy to a separate S3 account.)
    I’m playing with some stuff that allows multiple users to upload stuff to my S3 account. I haven’t (yet) figured out how to set up ‘signed’ uploads. I’m close using this (http://developer.amazonwebservices.com/connect/entry.jspa?externalID=1434) approach. I can generate the encoded policy and signature but can’t figure out how to get a command line utility to do the html post forms but I am making some progress. (http://www.autohotkey.com/forum/viewtopic.php?t=44844) so what I’ve done in the interim (really bad idea but used to test the rest of the application for now) is encode my secret key and when the user wants to upload the client requests the encoded key, decodes it and uploads the files. (the key is never saved in a file but still a bad idea) So I’m struggling with this a bit. (Until I get past the interim solution I was thinking of copying all the files to another S3 account that has a more secure key.)

    -- Steve on 5 June 2009

  237. Steve,

    Yes, if someone deletes the files, they’re gone. Amazon does not keep extra backup copies anywhere.

    I do HTTP POST form uploads myself fairly often; if you control the server there is no reason for the end-user to ever have possession of your secret key. However, if you’re wanting a command-line utility to do the uploads, you may consider writing your own web-based API that the command-line utility can hit instead of trying to post directly to S3. That way you can have separate credentials for each user (your web API would ask for those), and you wouldn’t ever have to give end-users your secret key.

    I realize writing your own web API with credentials isn’t trivial, but it’s not too difficult either; it may be the best solution to your problem, assuming you have access to a web server on which to run your API.

    -- Heron on 6 June 2009

  238. Heron,

    Yes, I’ve been trying to interact with the HTTPS POST method as described at: http://developer.amazonwebservices.com/connect/entry.jspa?externalID=1434 but am having trouble getting a straight forward client for my users. I’ve been working with cURL but that seems like there is no simple SSL binary but the need to install a bunch of MS software to make it work. Now I’m looking at some script to just plug in the parameters for a hidden IE window. (Seems kind of brute force but maybe the only way for me and my limited coding skills to pull it off.)

    -- Steve on 7 June 2009

  239. If you’ll send me an e-mail at heron@xnapid.com with a complete description of the program you’re trying to write, I might be able to point you in the right direction.

    -- Heron on 8 June 2009

  240. Thanks I will do that.

    -- Steve on 8 June 2009

  241. Is it possible to set a bandwidth limit for files upload?

    -- Fabrizio on 23 June 2009

  242. Fabrizio, that’s not something that is controlled by the S3 class.

    Depending on how you’re uploading the file, you might be able to control it through apache or php, or even your operating system or router, but it’s not something we can really help you with, sorry.

    -- Heron on 24 June 2009

Post Comment

Top

Copyright © 2007-2008 Undesigned