PUT / Copy with PHP, REST, Flex and Amazon S3

I have been trying for several weeks to correctly format a REST request in the Amazon AWS S3 API using the available examples on the Internet, but I could not even successfully connect.

I found the code to generate the signature, found the correct string formatting method for encoding and http headers . I worked the way through signatureDoesNotMatch errors to get the message Anonymous users can not perform copy functions, Please authenticate .

I have a working copy of an Adobe Flex application that successfully downloads files, but with their "original" file name. The point of using REST with the Amazon API is to execute a PUT (copy) of the file so that I can rename it to what my back system can use.

If I could find a way to get this REST application to work, or perhaps a way to specify the β€œnew” file name inside Flex at boot time, I could avoid all this REST situation all together.

If someone successfully executed the PUT/Copy command on the Amazon API via REST , I would be very interested in how this was done - OR - if someone was able to change the destination file name using Flex fileReference.browse() I will also endlessly grateful for any pointers.


The PHP code for this is as follows:

 $aws_key = 'removed_for_security'; $aws_secret = 'removed_for_security'; $source_file = $uploaded_s3_file; // file to upload to S3 (defined in above script) $aws_bucket = 'bucket'; // AWS bucket $aws_object = $event_file_name; // AWS object name (file name) if (strlen($aws_secret) != 40) die("$aws_secret should be exactly 40 bytes long"); $file_data = file_get_contents($source_file); if ($file_data == false) die("Failed to read file " . $source_file); // opening HTTP connection to Amazon S3 $fp = fsockopen("s3.amazonaws.com", 80, $errno, $errstr, 30); if (!$fp) die("$errstr ($errno)\n"); // Uploading object $file_length = strlen($file_data); // for Content-Length HTTP field $dt = gmdate('r'); // GMT based timestamp // preparing String to Sign (see AWS S3 Developer Guide) // preparing string to sign $string2sign = "PUT {$dt} /{$aws_bucket}/{$aws_object}"; // preparing HTTP query // $query = "PUT /".$aws_bucket."/".$event_file_name." HTTP/1.1 $query = "PUT /" . $event_file_name . " HTTP/1.1 Host: {$aws_bucket}.s3.amazonaws.com Date: {$dt} x-amz-copy-source: /{$aws_bucket}/{$current_s3_filename} x-amz-acl: public-read Authorization: AWS {$aws_key}:" . amazon_hmac($string2sign) . "\n\n"; $query .= $file_data; $resp = sendREST($fp, $query); if (strpos($resp, '') !== false) { die($resp); } echo "FILE uploaded\n"; // done echo "Your file URL is: http://s3.amazonaws.com/{$aws_bucket}/{$aws_object}\n"; fclose($fp); // Sending HTTP query and receiving, with trivial keep-alive support function sendREST($fp, $q, $debug = true){ if ($debug) echo "\nQUERY<<{$q}>>\n"; fwrite($fp, $q); $r = ''; $check_header = true; while (!feof($fp)) { $tr = fgets($fp, 256); if ($debug) echo "\nRESPONSE<<{$tr}>>"; $r .= $tr; if (($check_header) && (strpos($r, "\r\n\r\n") !== false)) { // if content-length == 0, return query result if (strpos($r, 'Content-Length: 0') !== false) { return $r; } } // Keep-alive responses does not return EOF // they end with \r\n0\r\n\r\n string if (substr($r, -7) == "\r\n0\r\n\r\n") { return $r; } } return $r; } // hmac-sha1 code START // hmac-sha1 function: assuming key is global $aws_secret 40 bytes long // read more at http://en.wikipedia.org/wiki/HMAC // warning: key($aws_secret) is padded to 64 bytes with 0x0 after first function call function amazon_hmac($stringToSign) { // helper function binsha1 for amazon_hmac (returns binary value of sha1 hash) if (!function_exists('binsha1')) { if (version_compare(phpversion(), "5.0.0", ">=")) { function binsha1($d) { return sha1($d, true); } } else { function binsha1($d) { return pack('H*', sha1($d)); } } } global $aws_secret; if (strlen($aws_secret) == 40) { $aws_secret = $aws_secret . str_repeat(chr(0), 24); } $ipad = str_repeat(chr(0x36), 64); $opad = str_repeat(chr(0x5c), 64); $hmac = binsha1(($aws_secret ^ $opad) . binsha1(($aws_secret ^ $ipad) . $stringToSign)); return base64_encode($hmac); } // hmac-sha1 code END 

When I send an invalid or invalid header, I get the corresponding error message as expected:

Request:

PUT / bucket / 1-132-1301047200-1.jpg HTTP / 1.1 Host: s3.amazonaws.com x-amz-acl: public-read Connection: keep-alive Content-Length: 34102 Date: Sat, March 26, 2011 00 : 43: 36 +0000 Authorization: AWS - removed for security: GmgRObHEFuirWPwaqRgdKiQK / EQ =

HTTP / 1.1 403 Forbidden
x-amz-request-id: A7CB0311812CD721
x-amz-id-2: ZUY0mH4Q20Izgt / 9BNhpJl9OoOCp59DKxlH2JJ6K + sksyxI8lFtmJrJOk1imxM / A
Content-Type: application / xml
Transmission Encoding: chunked
Date: Sat, March 26, 2011 00:43:36 GMT
Connection: close
Server: amazon3
397 SignatureDoesNotMatch The signature you signed for the request does not match the signature you provided. Check your key and signing method. 50 55 54 0a 0a 0a 53 61 74 2c 20 32 36 20 4d 61 72 20 32 30 31 31 20 30 30 3a 34 33 3a 33 36 20 2b 30 30 30 30 0a 78 2d 61 6d 7a 2d 61 63 6c 3a 70 75 62 6c 69 63 2d 72 65 61 64 0a 2f 6d 6c 68 2d 70 72 6f 64 75 63 74 69 6f 6e 2f 31 2d 31 33 32 2d 31 33 30 31 30 34 37 32 30 30 2d 31 2e 6a 70 67A7CB0311812CD721ZUY0mH4Q20Izgt / 9BNhpJlJJOJoKJOK6J1JKJOJ6J2KJ6KJ6KJ6KJ9JKJ2KJOK6 + sksyxI8lFtmJrJOk1imxM / AGmgRObHEFuirWPwaqRgdKiQK / EQ = PUT Sat, 26 Mar 2011 00:43:36 +0000 x-amz-acl: public-read / bucket / 1-132-1301047200-1.jpg-removed for security - 0

, but when sending correctly formatted requests, it says that I'm not authenticated:

Used query:

PUT / 1-132-1301047200-1.jpg HTTP / 1.1 Host: bucket.s3.amazonaws.com Date: Sat, Mar 26, 2011 00:41:50 +0000 x-amz-copy-source: / bucket / clock. jpg x-amz-acl: public-read Authorization: AWS -removed for security: BMiGhgbFnVAJyiderKjn1cT7cj4 =

HTTP / 1.1 403 Forbidden x-amz-request-id: ABE45FD4DFD19927
x-amz-id-2: CnkMmoF550H1zBlrwwKfN8zoOSt7r / zud8mRuLqzzBrdGguotcvrpZ3aU4HR4RoO
Content-Type: application / xml
Transmission Encoding: chunked
Date: Sat, March 26, 2011 00:41:50 GMT
Server: amazon3

Accessdenied
Anonymous users cannot copy objects. Please log in
ABE45FD4DFD19927CnkMmoF550H1zBlrwwKfN8zoOSt7r / zud8mRuLqzzBrdGguotcvrpZ3aU4HR4RoO 0
Date: Sat, March 26, 2011 00:41:50 GMT
Connection: close
Server: amazon3

+4
source share
1 answer

I have been trying for several weeks to correctly format a REST request in the Amazon AWS S3 API using available examples on the web

Have you tried the Amazon AWS SDK for PHP ? It is comprehensive, complete, and most importantly, written by Amazon. If their own code does not work for you, something will be really wrong.


Here is sample code using the linked SDK to load example.txt in the current directory into a bucket named 'my_very_first_bucket'.

 <?php // Complain wildly. ini_set('display_errors', true); error_reporting(-1); // Set these yourself. define('AWS_KEY', ''); define('AWS_SECRET_KEY', ''); // We'll assume that the SDK is in our current directory include_once 'sdk-1.3.1/sdk.class.php'; include_once 'sdk-1.3.1/services/s3.class.php'; // Set the bucket and name of the file we're sending. // It happens that we're actually uploading the file and // keeping the name, so we're re-using the variable // below. $bucket_name = 'my_very_first_bucket'; $file_to_upload = 'example.txt'; // Fire up the object $s3 = new AmazonS3(AWS_KEY, AWS_SECRET_KEY); // This returns a "CFResponse" $r = $s3->create_object( $bucket_name, $file_to_upload, array( // Filename of the thing we're uploading 'fileUpload' => (__DIR__ . '/' . $file_to_upload), // ACL'd public. 'acl' => AmazonS3::ACL_PUBLIC, // No wai. 'contentType' => 'text/plain', // The docs say it'll guess this, but may as well. 'length' => filesize(__DIR__ . '/' . $file_to_upload) ) ); // Did it work? echo "Worked: "; var_dump($r->isOK()); // Status as in HTTP. echo "\nStatus: "; var_dump($r->status); // The public URL by which we can reach this object. echo "\nURL: "; echo $s3->get_object_url($bucket_name, $file_to_upload); // Tada! echo "\n"; 

Relevant API docs:

You can navigate through the rest of the methods in the menu on the left. It is quite comprehensive, including creating a new bucket, managing, deleting, the same for objects, etc.

You should be able to basically abandon this code and whether to work correctly. PHP 5.2-safe.


Edit Silver Tiger:

Charles -

The method you provide uses the SDK API functions to load a file from the local file system into the bucket of my choice. I have this part already working through Flex, and the download works like a charm. This problem is to send an AWS S3 REST request to change the file name from its current β€œloaded” name to a new name, a more suitable name that will work with my end (database, tracking, etc., Which I process and display separately in PHP using MyySQL).

AWS S3 does not really support the β€œcopy” function, so they provided a method for re-β€œPUT” the file by reading the source from your own bucket and placing a new copy using a different name in the same bucket. The difficulty I ran into is handling the REST request, hence the HMAC encryption.

I really appreciate your time and understand the example that you provided, since I also have a working copy of the PHP download, which functioned before I developed the Flex application. The reason for Flex was the inclusion of status updates and a dynamically updated progress bar, which also works like a charm :).

I will continue to offer the REST solution from the point of view of Amason zupport, this will be the only way that I can rename the file that already exists in my bucket for each support team.

As always, if you have any input or suggestions regarding submitting a REST, I would appreciate any feedback.

Thanks,

Silver tiger


Copy / delete proof works:

  $r = $s3->copy_object( array( 'bucket' => $bucket_name, 'filename' => $file_to_upload ), array( 'bucket' => $bucket_name, 'filename' => 'foo.txt' ) ); // Did it work? echo "Worked: "; var_dump($r->isOK()); // Status as in HTTP. echo "\nStatus: "; var_dump($r->status); // The public URL by which we can reach this object. echo "\nURL: "; echo $s3->get_object_url($bucket_name, 'foo.txt'); echo "\nDelete: "; // Nuke? $r = $s3->delete_object($bucket_name, $file_to_upload); // Did it work? echo "Worked: "; var_dump($r->isOK()); // Status as in HTTP. echo "\nStatus: "; var_dump($r->status); 

Edit Silver Tiger:

Charles -

No need for REST, no worries ... SDK 1.3.1 and your help solves the problem. The code I used for testing is very similar to yours:

 // Complain wildly. ini_set('display_errors', true); error_reporting(-1); // Set these yourself. define('AWS_KEY', 'removed for security'); define('AWS_SECRET_KEY', 'removed for security'); // We'll assume that the SDK is in our current directory include_once 'includes/sdk-1.3.1/sdk.class.php'; include_once 'includes/sdk-1.3.1/services/s3.class.php'; // Set the bucket and name of the file we're sending. // It happens that we're actually uploading the file and // keeping the name, so we're re-using the variable // below. $bucket = 'bucket'; $file_to_upload = 'example.txt'; $Source_file_to_copy = 'Album.jpg'; $Destination_file = 'Album2.jpg'; // Fire up the object // Instantiate the class $s3 = new AmazonS3(); $response = $s3->copy_object( array( // Source 'bucket' => $bucket, 'filename' => $Source_file_to_copy ), array( // Destination 'bucket' => $bucket, 'filename' => $Destination_file ) ); // Success? var_dump($response->isOK()); 

Now I will perform the deletion after the copy, and we are gold. Thank you sir for your understanding and help.
Silver tiger

+8
source

Source: https://habr.com/ru/post/1345284/


All Articles