├── Lib
└── AmazonS3.php
├── README.md
└── Test
├── Case
└── Lib
│ └── AmazonS3Test.php
└── test_app
└── webroot
└── files
├── avatars
└── copify.png
├── csv_iso8859.csv
└── dots.csv
/Lib/AmazonS3.php:
--------------------------------------------------------------------------------
1 | accessKey, $this->secretKey, $this->bucket) = $config;
108 | // Set current date
109 | $this->setDate();
110 | }
111 |
112 | /**
113 | * Put a local file in an S3 bucket
114 | *
115 | * @return void
116 | * @author Rob Mcvey
117 | **/
118 | public function put($localPath , $remoteDir = null) {
119 |
120 | // Base filename
121 | $file = basename($localPath);
122 |
123 | // File remote/local files
124 | $this->checkLocalPath($localPath);
125 | $this->checkFile($file);
126 | $this->checkRemoteDir($remoteDir);
127 |
128 | // Signature
129 | $stringToSign = $this->stringToSign('put');
130 |
131 | // Build the HTTP request
132 | $request = array(
133 | 'method' => 'PUT',
134 | 'uri' => array(
135 | 'scheme' => 'https',
136 | 'host' => $this->bucket . '.' . $this->endpoint,
137 | 'path' => $this->file,
138 | ),
139 | 'header' => array(
140 | 'Accept' => '*/*',
141 | 'User-Agent' => 'CakePHP',
142 | 'Date' => $this->date,
143 | 'Authorization' => 'AWS ' . $this->accessKey . ':' . $this->signature($stringToSign),
144 | 'Content-MD5' => $this->contentMd5,
145 | 'Content-Type' => $this->contentType,
146 | 'Content-Length' => $this->File->size()
147 | ),
148 | 'body' => $this->File->read()
149 | );
150 |
151 | // Any addional Amazon headers to add?
152 | $request = $this->addAmazonHeadersToRequest($request);
153 |
154 | // Make the HTTP request
155 | $response = $this->handleRequest($request);
156 |
157 | // Handle response errors if any
158 | $this->handleResponse($response);
159 | }
160 |
161 | /**
162 | * Fetch a remote file from S3 and save locally
163 | *
164 | * @return void
165 | * @author Rob Mcvey
166 | **/
167 | public function get($file , $localPath) {
168 | // File remote/local files
169 | $this->checkFile($file);
170 | $this->checkLocalPath($localPath);
171 |
172 | // Signature
173 | $stringToSign = $this->stringToSign('get');
174 |
175 | $request = array(
176 | 'method' => 'GET',
177 | 'uri' => array(
178 | 'scheme' => 'https',
179 | 'host' => $this->bucket . '.' . $this->endpoint,
180 | 'path' => $this->file,
181 | ),
182 | 'header' => array(
183 | 'Accept' => '*/*',
184 | 'User-Agent' => 'CakePHP',
185 | 'Date' => $this->date,
186 | 'Authorization' => 'AWS ' . $this->accessKey . ':' . $this->signature($stringToSign)
187 | )
188 | );
189 |
190 | // Make the HTTP request
191 | $response = $this->handleRequest($request);
192 |
193 | // Handle response errors if any
194 | $this->handleResponse($response);
195 |
196 | // Write file locally
197 | $this->File = new File($this->localPath . DS . $this->file, true);
198 | $this->File->write($response->body);
199 | }
200 |
201 | /**
202 | * Delete a remote file from S3
203 | *
204 | * @return void
205 | * @author Rob Mcvey
206 | **/
207 | public function delete($file) {
208 | // File remote/local files
209 | $this->checkFile($file);
210 |
211 | // Signature
212 | $stringToSign = $this->stringToSign('delete');
213 |
214 | $request = array(
215 | 'method' => 'DELETE',
216 | 'uri' => array(
217 | 'scheme' => 'https',
218 | 'host' => $this->bucket . '.' . $this->endpoint,
219 | 'path' => $this->file,
220 | ),
221 | 'header' => array(
222 | 'Accept' => '*/*',
223 | 'User-Agent' => 'CakePHP',
224 | 'Date' => $this->date,
225 | 'Authorization' => 'AWS ' . $this->accessKey . ':' . $this->signature($stringToSign)
226 | )
227 | );
228 |
229 | // Make the HTTP request
230 | $response = $this->handleRequest($request);
231 |
232 | // Handle response errors if any
233 | $this->handleResponse($response);
234 | }
235 |
236 | /**
237 | * Sets the date we're working with. Used in both HTTP request and signature.
238 | *
239 | * @return void
240 | * @author Rob Mcvey
241 | **/
242 | public function setDate($date = null) {
243 | if (!$date) {
244 | $this->date = gmdate('D, d M Y H:i:s \G\M\T');
245 | } else {
246 | $this->date = $date;
247 | }
248 | }
249 |
250 | /**
251 | * Returns the public URL of a file
252 | *
253 | * @return string
254 | * @author Rob Mcvey
255 | **/
256 | public function publicUrl($file, $ssl = false) {
257 | $scheme = 'http';
258 | if ($ssl) {
259 | $scheme .= 's';
260 | }
261 | // Replace any preceeding slashes
262 | $file = preg_replace("/^\//" , '', $file);
263 | return sprintf('%s://%s.s3.amazonaws.com/%s' , $scheme , $this->bucket, $file);
264 | }
265 |
266 | /**
267 | * Makes the HTTP Rest request
268 | *
269 | * @return void
270 | * @author Rob Mcvey
271 | **/
272 | public function handleRequest($request) {
273 | // HttpSocket.
274 | if (!$this->HttpSocket) {
275 | $this->HttpSocket = new HttpSocket();
276 | $this->HttpSocket->quirksMode = true; // Amazon returns sucky XML
277 | }
278 |
279 | // Make request
280 | try {
281 | return $this->HttpSocket->request($request);
282 | } catch (SocketException $e) {
283 | // If error Amazon returns garbage XML and
284 | // throws HttpSocket::_decodeChunkedBody - Could not parse malformed chunk ???
285 | throw new AmazonS3Exception(__('Could not complete the HTTP request'));
286 | }
287 | }
288 |
289 | /**
290 | * Handles the HttpSocket response object and checks for any errors
291 | *
292 | * @return void
293 | * @author Rob Mcvey
294 | **/
295 | public function handleResponse($response) {
296 | if (!property_exists($response , 'code')) {
297 | throw new InvalidArgumentException(__('The response from Amazon S3 is invalid'));
298 | }
299 | // All good
300 | if (in_array($response->code, array(200, 204))) {
301 | return $response->code;
302 | } else {
303 | $headers = $response->headers;
304 | $genericMessage = __('There was an error communicating with AWS');
305 | if (isset($headers['Content-Type']) && $headers['Content-Type'] == 'application/xml') {
306 | $xml = $response->body;
307 | $Xml = Xml::build($xml);
308 | if (property_exists($Xml, 'Message')) {
309 | throw new AmazonS3Exception($Xml->Message);
310 | } else {
311 | throw new AmazonS3Exception($genericMessage);
312 | }
313 | } else {
314 | throw new AmazonS3Exception($genericMessage);
315 | }
316 | }
317 | }
318 |
319 | /**
320 | * Check we have a file string
321 | *
322 | * @return void
323 | * @author Rob Mcvey
324 | **/
325 | public function checkFile($file) {
326 | $this->file = $file;
327 | // Set the target and local path to where we're saving
328 | if (empty($this->file)) {
329 | throw new InvalidArgumentException(__('You must specify the file you are fetching (e.g remote_dir/file.txt)'));
330 | }
331 | }
332 |
333 | /**
334 | * Check our local path
335 | *
336 | * @return void
337 | * @author Rob Mcvey
338 | **/
339 | public function checkLocalPath($localPath) {
340 | $this->localPath = $localPath;
341 | if (empty($this->localPath)) {
342 | throw new InvalidArgumentException(__('You must set a localPath (i.e where to save the file)'));
343 | }
344 | if (!file_exists($this->localPath)) {
345 | throw new InvalidArgumentException(__('The localPath you set does not exist'));
346 | }
347 | }
348 |
349 | /**
350 | * Removes preceeding/trailing slashes from a remote dir target and builds $this->file again
351 | *
352 | * @return void
353 | * @author Rob Mcvey
354 | **/
355 | public function checkRemoteDir($remoteDir) {
356 | // Is the target a directory? Remove preceending and trailing /
357 | if ($remoteDir) {
358 | $remoteDir = preg_replace(array("/^\//", "/\/$/") , "", $remoteDir);
359 | $this->file = $remoteDir . '/' . $this->file;
360 | }
361 | }
362 |
363 | /**
364 | * Creates the Authorization header string to sign
365 | *
366 | * @return void
367 | * @author Rob Mcvey
368 | **/
369 | public function stringToSign($method = 'get') {
370 |
371 | // PUT object, need hash and content type
372 | if (strtoupper($method) == 'PUT') {
373 | $this->getLocalFileInfo();
374 | $this->contentType = $this->info['mime'];
375 | $this->contentMd5 = $this->getContentMd5();
376 | }
377 |
378 | // Add any additional Amazon specific headers if present
379 | $this->buildAmazonHeaders();
380 |
381 | // stringToSign
382 | $toSign = strtoupper($method) . "\n";
383 | $toSign .= $this->contentMd5 . "\n";
384 | $toSign .= $this->contentType . "\n";
385 | $toSign .= $this->date . "\n";
386 | $toSign .= $this->canonicalizedAmzHeaders;
387 | $toSign .= '/' . $this->bucket . '/' . $this->file;
388 | return $toSign;
389 | }
390 |
391 | /**
392 | * Takes the request array pre-put and adds any additional amazon headers
393 | *
394 | * @return void
395 | * @author Rob Mcvey
396 | **/
397 | public function addAmazonHeadersToRequest($request) {
398 | if (!empty($this->amazonHeaders) && is_array($this->amazonHeaders)) {
399 | foreach ($this->amazonHeaders as $k => $header) {
400 | $request['header'][$k] =$header;
401 | }
402 | }
403 | return $request;
404 | }
405 |
406 | /**
407 | * Add any additional Amazon specific headers if present
408 | *
409 | * @return void
410 | * @author Rob Mcvey
411 | **/
412 | public function buildAmazonHeaders() {
413 | if (!empty($this->amazonHeaders) && is_array($this->amazonHeaders)) {
414 | $this->canonicalizedAmzHeaders = '';
415 | $this->sortLexicographically();
416 | foreach ($this->amazonHeaders as $k => $header) {
417 | $this->canonicalizedAmzHeaders .= strtolower($k) . ":" . $header . "\n";
418 | }
419 | }
420 | }
421 |
422 | /**
423 | * Sort the collection of headers lexicographically by header name.
424 | *
425 | * @return void
426 | * @author Rob Mcvey
427 | **/
428 | public function sortLexicographically() {
429 | ksort($this->amazonHeaders, SORT_FLAG_CASE | SORT_NATURAL);
430 | }
431 |
432 | /**
433 | * Create signature for the Authorization header.
434 | * Format: Base64( HMAC-SHA1( YourSecretAccessKeyID, UTF-8-Encoding-Of( StringToSign ) ) );
435 | * @param string the header string to sign
436 | * @return string base64_encode encoded string
437 | * @link http://docs.aws.amazon.com/AmazonS3/latest/dev/RESTAuthentication.html
438 | */
439 | public function signature($stringToSign) {
440 | return base64_encode(hash_hmac('sha1', $stringToSign, $this->secretKey, true));
441 | }
442 |
443 | /**
444 | * Get local file info (uses CakePHP Utility/File class)
445 | *
446 | * @return array
447 | * @author Rob Mcvey
448 | **/
449 | public function getLocalFileInfo() {
450 | $this->File = new File($this->localPath);
451 | $this->info = $this->File->info();
452 | return $this->info;
453 | }
454 |
455 | /**
456 | * Return base64 encoded file checksum
457 | *
458 | * @return string
459 | * @author Rob Mcvey
460 | **/
461 | public function getContentMd5() {
462 | return base64_encode(md5_file($this->localPath , true));
463 | }
464 |
465 | }
466 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Amazon S3 Plugin for CakePHP 2.x
2 |
3 | A CakePHP Plugin to interact with Amazon Web Services S3 objects. This plugin provides a simple and robust methods that can be added to any CakePHP project to complete the following:
4 |
5 | * Retrieve a remote file from an S3 bucket and save locally
6 | * Save a local file in an S3 bucket
7 | * Delete a file in an S3 bucket
8 |
9 | ### Requirements
10 |
11 | * CakePHP 2.x
12 | * An Amazon Web Services account (http://aws.amazon.com/s3/)
13 | * Your AWS access key and secret key
14 |
15 | ### Installation
16 |
17 | _[Manual]_
18 |
19 | * Download this: [http://github.com/robmcvey/cakephp-amazon-s3/zipball/master](http://github.com/robmcvey/cakephp-amazon-s3/zipball/master)
20 | * Unzip that download.
21 | * Copy the resulting folder to `app/Plugin`
22 | * Rename the folder you just copied to `AmazonS3`
23 |
24 | _[GIT Submodule]_
25 |
26 | In your app directory type:
27 |
28 | ```shell
29 | git submodule add -b master git://github.com/robmcvey/cakephp-amazon-s3.git Plugin/AmazonS3
30 | git submodule init
31 | git submodule update
32 | ```
33 |
34 | _[GIT Clone]_
35 |
36 | In your `Plugin` directory type:
37 |
38 | ```shell
39 | git clone -b master git://github.com/robmcvey/cakephp-amazon-s3.git AmazonS3
40 | ```
41 |
42 | ### Usuage examples
43 |
44 | Remember to add `CakePlugin::load('AmazonS3');` to your app's bootstrap file.
45 |
46 | Then, simply initialise the plugin with your AWS Access key, secret key and the bucket name you wish to work with.
47 |
48 | ```php
49 | App::uses('AmazonS3', 'AmazonS3.Lib');
50 | $AmazonS3 = new AmazonS3(array('{access key}', '{secret key}', '{bucket name}'));
51 | ```
52 |
53 | ### GET
54 |
55 | The `get` method retrieves a remote file and saves it locally. So let's say there is the file `foo.jpg` on S3 and you want to save it locally in `/home/me/stuff/photos` you'd use the following.
56 |
57 | ```php
58 | $AmazonS3->get('foo.jpg' , '/home/me/stuff/photos');
59 | ```
60 |
61 | ### PUT
62 |
63 | The `put` method does the reverse of `get`, and saves a local file to S3.
64 |
65 | ```php
66 | $AmazonS3->put('/home/me/stuff/photos/foo.jpg');
67 | ```
68 |
69 | You can optionally specifiy a remote directory within the bucket to save the file in. Be sure not to include a starting / in the remote directory string.
70 |
71 | ```php
72 | $AmazonS3->put('/home/me/stuff/photos/foo.jpg' , 'some/folder');
73 | ```
74 |
75 | To add any additional AWS headers to a `put`, example to set the file as "public", they can be passed as an array to the `amazonHeaders` property.
76 |
77 | ```php
78 | $AmazonS3->amazonHeaders = array(
79 | 'x-amz-acl' => 'public-read',
80 | 'X-Amz-Meta-ReviewedBy' => 'john.doe@yahoo.biz'
81 | );
82 | $AmazonS3->put('/home/me/stuff/photos/foo.jpg' , 'some/folder');
83 | ```
84 |
85 | ### DELETE
86 |
87 | Deletes a file from S3.
88 |
89 | ```php
90 | $AmazonS3->delete('foo.jpg');
91 | ```
92 |
93 | Or delete from within a directory in the bucket. Be sure not to include a starting / in the string or you will receive a SignatureDoesNotMatch error:
94 |
95 | ```php
96 | $AmazonS3->delete('some/folder/foo.jpg');
97 | ```
98 |
--------------------------------------------------------------------------------
/Test/Case/Lib/AmazonS3Test.php:
--------------------------------------------------------------------------------
1 | AmazonS3 = new AmazonS3(array('foo' , 'bar', 'bucket'));
34 | }
35 |
36 | /**
37 | * tearDown method
38 | *
39 | * @return void
40 | */
41 | public function tearDown() {
42 | parent::tearDown();
43 | unset($this->AmazonS3);
44 | }
45 |
46 | /**
47 | * testConstructor
48 | *
49 | * @return void
50 | * @author Rob Mcvey
51 | **/
52 | public function testConstructor() {
53 | $this->assertEqual('foo' , $this->AmazonS3->accessKey);
54 | $this->assertEqual('bar' , $this->AmazonS3->secretKey);
55 | $this->assertEqual('bucket' , $this->AmazonS3->bucket);
56 | $this->assertEqual(date('Y') , date('Y' , strtotime($this->AmazonS3->date)));
57 | }
58 |
59 | /**
60 | * testHandleResponse
61 | *
62 | * @return void
63 | * @author Rob Mcvey
64 | **/
65 | public function testHandleResponse() {
66 | // Mock the HttpSocket response
67 | $HttpSocketResponse = new stdClass();
68 | $HttpSocketResponse->body = '????JFIFdd??Duckya??Adobed??????????';
69 | $HttpSocketResponse->code = 200;
70 | $HttpSocketResponse->reasonPhrase = 'OK';
71 | $HttpSocketResponse->headers = array(
72 | 'x-amz-id-2' => '4589328529385938',
73 | 'x-amz-request-id' => 'GSDFGt45egdfsC',
74 | 'Date' => 'Mon, 23 Sep 2013 08:46:05 GMT',
75 | 'Last-Modified' => 'Tue, 29 Nov 2011 10:30:03 GMT',
76 | 'ETag' => '24562346dgdgsdgf2352"',
77 | 'Accept-Ranges' => 'bytes',
78 | 'Content-Type' => 'image/jpeg',
79 | 'Content-Length' => 36410,
80 | 'Connection' => 'close',
81 | 'Server' => 'AmazonS3'
82 | );
83 | $result = $this->AmazonS3->handleResponse($HttpSocketResponse);
84 | $this->assertEqual(200 , $result);
85 | }
86 |
87 | /**
88 | * testHandleResponseBadFormat
89 | *
90 | * @return void
91 | * @author Rob Mcvey
92 | * @expectedException InvalidArgumentException
93 | * @expectedExceptionMessage The response from Amazon S3 is invalid
94 | **/
95 | public function testHandleResponseBadFormat() {
96 | // Mock the HttpSocket response
97 | $HttpSocketResponse = new stdClass();
98 | $HttpSocketResponse->body = '????JFIFdd??Duckya??Adobed??????????';
99 | $HttpSocketResponse->reasonPhrase = 'Foo';
100 | $HttpSocketResponse->headers = array(
101 | 'x-amz-request-id' => 'GSDFGt45egdfsC',
102 | );
103 | $result = $this->AmazonS3->handleResponse($HttpSocketResponse);
104 | }
105 |
106 | /**
107 | * testPut
108 | *
109 | * @return void
110 | * @author Rob Mcvey
111 | **/
112 | public function testPut() {
113 | $file_path = APP . 'Plugin' . DS . 'AmazonS3' . DS . 'Test' . DS . 'test_app' . DS . 'webroot' . DS . 'files' . DS . 'dots.csv';
114 | $this->AmazonS3->setDate('Mon, 23 Sep 2013 08:46:05 GMT');
115 |
116 | // Mock the built request
117 | $expectedRequest = array(
118 | 'method' => 'PUT',
119 | 'uri' => array(
120 | 'host' => 'bucket.s3.amazonaws.com',
121 | 'scheme' => 'https',
122 | 'path' => 'dots.csv'
123 | ),
124 | 'header' => array(
125 | 'Accept' => '*/*',
126 | 'User-Agent' => 'CakePHP',
127 | 'Date' => 'Mon, 23 Sep 2013 08:46:05 GMT',
128 | 'Authorization' => 'AWS foo:9Lsir9m2y16ffUi6v+KlRe0pGdA=',
129 | 'Content-MD5' => 'L0O0L9gz0ed0IKja50GQAA==',
130 | 'Content-Type' => 'text/plain',
131 | 'Content-Length' => 3
132 | ),
133 | 'body' => '...'
134 | );
135 |
136 | // Mock the HttpSocket response
137 | $HttpSocketResponse = new stdClass();
138 | $HttpSocketResponse->body = '????JFIFdd??Duckya??Adobed??????????';
139 | $HttpSocketResponse->code = 200;
140 | $HttpSocketResponse->reasonPhrase = 'OK';
141 | $HttpSocketResponse->headers = array(
142 | 'x-amz-id-2' => '4589328529385938',
143 | 'x-amz-request-id' => 'GSDFGt45egdfsC',
144 | 'Date' => 'Mon, 23 Sep 2013 08:46:05 GMT',
145 | 'Last-Modified' => 'Tue, 29 Nov 2011 10:30:03 GMT',
146 | 'ETag' => '24562346dgdgsdgf2352"',
147 | 'Accept-Ranges' => 'bytes',
148 | 'Content-Type' => 'image/jpeg',
149 | 'Content-Length' => 0,
150 | 'Connection' => 'close',
151 | 'Server' => 'AmazonS3'
152 | );
153 |
154 | // Mock the HttpSocket class
155 | $this->AmazonS3->HttpSocket = $this->getMock('HttpSocket');
156 | $this->AmazonS3->HttpSocket->expects($this->once())
157 | ->method('request')
158 | ->with($expectedRequest)
159 | ->will($this->returnValue($HttpSocketResponse));
160 |
161 | $this->AmazonS3->put($file_path);
162 | }
163 |
164 | /**
165 | * testPutWithDir
166 | *
167 | * @return void
168 | * @author Rob Mcvey
169 | **/
170 | public function testPutWithDir() {
171 | $file_path = APP . 'Plugin' . DS . 'AmazonS3' . DS . 'Test' . DS . 'test_app' . DS . 'webroot' . DS . 'files' . DS . 'dots.csv';
172 | $this->AmazonS3->setDate('Mon, 23 Sep 2013 08:46:05 GMT');
173 |
174 | // Mock the built request
175 | $expectedRequest = array(
176 | 'method' => 'PUT',
177 | 'uri' => array(
178 | 'host' => 'bucket.s3.amazonaws.com',
179 | 'scheme' => 'https',
180 | 'path' => 'some/dir/in/the/bucket/dots.csv'
181 | ),
182 | 'header' => array(
183 | 'Accept' => '*/*',
184 | 'User-Agent' => 'CakePHP',
185 | 'Date' => 'Mon, 23 Sep 2013 08:46:05 GMT',
186 | 'Authorization' => 'AWS foo:mCE9RQ8UJYRItzike6XZFd7XjcI=',
187 | 'Content-MD5' => 'L0O0L9gz0ed0IKja50GQAA==',
188 | 'Content-Type' => 'text/plain',
189 | 'Content-Length' => 3
190 | ),
191 | 'body' => '...'
192 | );
193 |
194 | // Mock the HttpSocket response
195 | $HttpSocketResponse = new stdClass();
196 | $HttpSocketResponse->body = '????JFIFdd??Duckya??Adobed??????????';
197 | $HttpSocketResponse->code = 200;
198 | $HttpSocketResponse->reasonPhrase = 'OK';
199 | $HttpSocketResponse->headers = array(
200 | 'x-amz-id-2' => '4589328529385938',
201 | 'x-amz-request-id' => 'GSDFGt45egdfsC',
202 | 'Date' => 'Mon, 23 Sep 2013 08:46:05 GMT',
203 | 'Last-Modified' => 'Tue, 29 Nov 2011 10:30:03 GMT',
204 | 'ETag' => '24562346dgdgsdgf2352"',
205 | 'Accept-Ranges' => 'bytes',
206 | 'Content-Type' => 'image/jpeg',
207 | 'Content-Length' => 0,
208 | 'Connection' => 'close',
209 | 'Server' => 'AmazonS3'
210 | );
211 |
212 | // Mock the HttpSocket class
213 | $this->AmazonS3->HttpSocket = $this->getMock('HttpSocket');
214 | $this->AmazonS3->HttpSocket->expects($this->once())
215 | ->method('request')
216 | ->with($expectedRequest)
217 | ->will($this->returnValue($HttpSocketResponse));
218 |
219 | $this->AmazonS3->put($file_path , '/some/dir/in/the/bucket/');
220 | }
221 |
222 | /**
223 | * testPutWithMoreHeaders
224 | *
225 | * @return void
226 | * @author Rob Mcvey
227 | **/
228 | public function testPutWithMoreHeaders() {
229 | $file_path = APP . 'Plugin' . DS . 'AmazonS3' . DS . 'Test' . DS . 'test_app' . DS . 'webroot' . DS . 'files' . DS . 'dots.csv';
230 | $this->AmazonS3->setDate('Mon, 23 Sep 2013 08:46:05 GMT');
231 |
232 | // Mock the built request
233 | $expectedRequest = array(
234 | 'method' => 'PUT',
235 | 'uri' => array(
236 | 'host' => 'bucket.s3.amazonaws.com',
237 | 'scheme' => 'https',
238 | 'path' => 'some/dir/in/the/bucket/dots.csv'
239 | ),
240 | 'header' => array(
241 | 'Accept' => '*/*',
242 | 'User-Agent' => 'CakePHP',
243 | 'Date' => 'Mon, 23 Sep 2013 08:46:05 GMT',
244 | 'Authorization' => 'AWS foo:WxdnOvuaK37BwO72xShLSFu80LI=',
245 | 'Content-MD5' => 'L0O0L9gz0ed0IKja50GQAA==',
246 | 'Content-Type' => 'text/plain',
247 | 'Content-Length' => 3,
248 | 'X-Amz-Meta-ReviewedBy' => 'john.doe@yahoo.biz',
249 | 'x-amz-acl' => 'public-read',
250 | ),
251 | 'body' => '...'
252 | );
253 |
254 | // Mock the HttpSocket response
255 | $HttpSocketResponse = new stdClass();
256 | $HttpSocketResponse->body = '????JFIFdd??Duckya??Adobed??????????';
257 | $HttpSocketResponse->code = 200;
258 | $HttpSocketResponse->reasonPhrase = 'OK';
259 | $HttpSocketResponse->headers = array(
260 | 'x-amz-id-2' => '4589328529385938',
261 | 'x-amz-request-id' => 'GSDFGt45egdfsC',
262 | 'Date' => 'Mon, 23 Sep 2013 08:46:05 GMT',
263 | 'Last-Modified' => 'Tue, 29 Nov 2011 10:30:03 GMT',
264 | 'ETag' => '24562346dgdgsdgf2352"',
265 | 'Accept-Ranges' => 'bytes',
266 | 'Content-Type' => 'image/jpeg',
267 | 'Content-Length' => 0,
268 | 'Connection' => 'close',
269 | 'Server' => 'AmazonS3'
270 | );
271 |
272 | // Mock the HttpSocket class
273 | $this->AmazonS3->HttpSocket = $this->getMock('HttpSocket');
274 | $this->AmazonS3->HttpSocket->expects($this->once())
275 | ->method('request')
276 | ->with($expectedRequest)
277 | ->will($this->returnValue($HttpSocketResponse));
278 |
279 | $this->AmazonS3->amazonHeaders = array(
280 | 'x-amz-acl' => 'public-read',
281 | 'X-Amz-Meta-ReviewedBy' => 'john.doe@yahoo.biz',
282 | );
283 |
284 | $this->AmazonS3->put($file_path , '/some/dir/in/the/bucket/');
285 | }
286 |
287 | /**
288 | * testGet
289 | *
290 | * @return void
291 | * @author Rob Mcvey
292 | **/
293 | public function testGet() {
294 | $localPath = APP . 'Plugin' . DS . 'AmazonS3' . DS . 'Test' . DS . 'test_app' . DS . 'webroot' . DS . 'files';
295 | $file = 'lolcat.jpg';
296 | $fullpath = $localPath . DS . $file;
297 | $this->AmazonS3->setDate('Mon, 23 Sep 2013 08:46:05 GMT');
298 |
299 | // Mock the built request
300 | $expectedRequest = array(
301 | 'method' => 'GET',
302 | 'uri' => array(
303 | 'host' => 'bucket.s3.amazonaws.com',
304 | 'scheme' => 'https',
305 | 'path' => 'lolcat.jpg'
306 | ),
307 | 'header' => array(
308 | 'Accept' => '*/*',
309 | 'User-Agent' => 'CakePHP',
310 | 'Date' => 'Mon, 23 Sep 2013 08:46:05 GMT',
311 | 'Authorization' => 'AWS foo:diD8K7IEVSvuywBxAoERbEu1rXM=',
312 | )
313 | );
314 |
315 | // Mock the HttpSocket response
316 | $HttpSocketResponse = new stdClass();
317 | $HttpSocketResponse->body = '????JFIFdd??Duckya??Adobed??????????';
318 | $HttpSocketResponse->code = 200;
319 | $HttpSocketResponse->reasonPhrase = 'OK';
320 | $HttpSocketResponse->headers = array(
321 | 'x-amz-id-2' => '4589328529385938',
322 | 'x-amz-request-id' => 'GSDFGt45egdfsC',
323 | 'Date' => 'Mon, 23 Sep 2013 08:46:05 GMT',
324 | 'Last-Modified' => 'Tue, 29 Nov 2011 10:30:03 GMT',
325 | 'ETag' => '24562346dgdgsdgf2352"',
326 | 'Accept-Ranges' => 'bytes',
327 | 'Content-Type' => 'image/jpeg',
328 | 'Content-Length' => '36410',
329 | 'Connection' => 'close',
330 | 'Server' => 'AmazonS3'
331 | );
332 |
333 | // Mock the HttpSocket class
334 | $this->AmazonS3->HttpSocket = $this->getMock('HttpSocket');
335 | $this->AmazonS3->HttpSocket->expects($this->once())
336 | ->method('request')
337 | ->with($expectedRequest)
338 | ->will($this->returnValue($HttpSocketResponse));
339 |
340 | $this->AmazonS3->get($file , $localPath);
341 | $this->assertFileExists($fullpath);
342 |
343 | $this->assertEquals('????JFIFdd??Duckya??Adobed??????????' , file_get_contents($fullpath));
344 | unlink($fullpath); // Bye bye
345 | }
346 |
347 | /**
348 | * testDelete
349 | *
350 | * @return void
351 | * @author Rob Mcvey
352 | **/
353 | public function testDelete() {
354 | $file = 'public/dots.csv';
355 | $this->AmazonS3->setDate('Mon, 23 Sep 2013 08:46:05 GMT');
356 |
357 | // Mock the built request
358 | $expectedRequest = array(
359 | 'method' => 'DELETE',
360 | 'uri' => array(
361 | 'host' => 'bucket.s3.amazonaws.com',
362 | 'scheme' => 'https',
363 | 'path' => 'public/dots.csv'
364 | ),
365 | 'header' => array(
366 | 'Accept' => '*/*',
367 | 'User-Agent' => 'CakePHP',
368 | 'Date' => 'Mon, 23 Sep 2013 08:46:05 GMT',
369 | 'Authorization' => 'AWS foo:+UdJAQGhwJf06KRWWIXumX/5V4Y=',
370 | )
371 | );
372 |
373 | // Mock the HttpSocket response
374 | $HttpSocketResponse = new stdClass();
375 | $HttpSocketResponse->body = '';
376 | $HttpSocketResponse->code = 204;
377 | $HttpSocketResponse->reasonPhrase = 'No Content';
378 | $HttpSocketResponse->headers = array(
379 | 'x-amz-id-2' => '4589328529385938',
380 | 'x-amz-request-id' => 'GSDFGt45egdfsC',
381 | 'Date' => 'Mon, 23 Sep 2013 08:46:05 GMT',
382 | 'Connection' => 'close',
383 | 'Server' => 'AmazonS3'
384 | );
385 |
386 | // Mock the HttpSocket class
387 | $this->AmazonS3->HttpSocket = $this->getMock('HttpSocket');
388 | $this->AmazonS3->HttpSocket->expects($this->once())
389 | ->method('request')
390 | ->with($expectedRequest)
391 | ->will($this->returnValue($HttpSocketResponse));
392 |
393 | $this->AmazonS3->delete($file);
394 | }
395 |
396 | /**
397 | * testGetException
398 | *
399 | * @return void
400 | * @author Rob Mcvey
401 | * @expectedException AmazonS3Exception
402 | * @expectedExceptionMessage The AWS Access Key Id you provided does not exist in our records.
403 | **/
404 | public function testGetException() {
405 | $localPath = APP . 'Plugin' . DS . 'AmazonS3' . DS . 'Test' . DS . 'test_app' . DS . 'webroot' . DS . 'files';
406 | $file = 'lolcat.jpg';
407 | $fullpath = $localPath . DS . $file;
408 | $this->AmazonS3->setDate('Mon, 23 Sep 2013 08:46:05 GMT');
409 |
410 | // Mock the built request
411 | $expectedRequest = array(
412 | 'method' => 'GET',
413 | 'uri' => array(
414 | 'host' => 'bucket.s3.amazonaws.com',
415 | 'scheme' => 'https',
416 | 'path' => 'lolcat.jpg'
417 | ),
418 | 'header' => array(
419 | 'Accept' => '*/*',
420 | 'User-Agent' => 'CakePHP',
421 | 'Date' => 'Mon, 23 Sep 2013 08:46:05 GMT',
422 | 'Authorization' => 'AWS foo:diD8K7IEVSvuywBxAoERbEu1rXM=',
423 | )
424 | );
425 |
426 | // Mock the HttpSocket response
427 | $HttpSocketResponse = new stdClass();
428 | $HttpSocketResponse->body = 'InvalidAccessKeyId
The AWS Access Key Id you provided does not exist in our records.452345DFSG3425foo';
429 | $HttpSocketResponse->code = 403;
430 | $HttpSocketResponse->headers = array(
431 | 'x-amz-id-2' => '4589328529385938',
432 | 'x-amz-request-id' => 'GSDFGt45egdfsC',
433 | 'Date' => 'Mon, 23 Sep 2013 08:46:05 GMT',
434 | 'Accept-Ranges' => 'bytes',
435 | 'Content-Type' => 'application/xml',
436 | 'Transfer-Encoding' => 'chunked',
437 | 'Connection' => 'close',
438 | 'Server' => 'AmazonS3'
439 | );
440 |
441 | // Mock the HttpSocket class
442 | $this->AmazonS3->HttpSocket = $this->getMock('HttpSocket');
443 | $this->AmazonS3->HttpSocket->expects($this->once())
444 | ->method('request')
445 | ->with($expectedRequest)
446 | ->will($this->returnValue($HttpSocketResponse));
447 |
448 | $this->AmazonS3->get($file, $localPath);
449 | $this->assertFalse(file_exists($fullpath));
450 | unlink($fullpath); // Bye bye just in case
451 | }
452 |
453 | /**
454 | * testGetGenericException
455 | *
456 | * @return void
457 | * @author Rob Mcvey
458 | * @expectedException AmazonS3Exception
459 | * @expectedExceptionMessage There was an error communicating with AWS
460 | **/
461 | public function testGetGenericException() {
462 | $localPath = APP . 'Plugin' . DS . 'AmazonS3' . DS . 'Test' . DS . 'test_app' . DS . 'webroot' . DS . 'files';
463 | $file = 'lolcat.jpg';
464 | $fullpath = $localPath . DS . $file;
465 | $this->AmazonS3->setDate('Mon, 23 Sep 2013 08:46:05 GMT');
466 |
467 | // Mock the built request
468 | $expectedRequest = array(
469 | 'method' => 'GET',
470 | 'uri' => array(
471 | 'host' => 'bucket.s3.amazonaws.com',
472 | 'scheme' => 'https',
473 | 'path' => 'lolcat.jpg'
474 | ),
475 | 'header' => array(
476 | 'Accept' => '*/*',
477 | 'User-Agent' => 'CakePHP',
478 | 'Date' => 'Mon, 23 Sep 2013 08:46:05 GMT',
479 | 'Authorization' => 'AWS foo:diD8K7IEVSvuywBxAoERbEu1rXM=',
480 | )
481 | );
482 |
483 | // Mock the HttpSocket response
484 | $HttpSocketResponse = new stdClass();
485 | $HttpSocketResponse->body = 'Woops
2342134123DFHGDHGDHDFGHfoo';
486 | $HttpSocketResponse->code = 403;
487 | $HttpSocketResponse->headers = array(
488 | 'x-amz-id-2' => '4589328529385938',
489 | 'x-amz-request-id' => 'GSDFGt45egdfsC',
490 | 'Date' => 'Mon, 23 Sep 2013 08:46:05 GMT',
491 | 'Accept-Ranges' => 'bytes',
492 | 'Content-Type' => 'application/xml',
493 | 'Transfer-Encoding' => 'chunked',
494 | 'Connection' => 'close',
495 | 'Server' => 'AmazonS3'
496 | );
497 |
498 | // Mock the HttpSocket class
499 | $this->AmazonS3->HttpSocket = $this->getMock('HttpSocket');
500 | $this->AmazonS3->HttpSocket->expects($this->once())
501 | ->method('request')
502 | ->with($expectedRequest)
503 | ->will($this->returnValue($HttpSocketResponse));
504 |
505 | $this->AmazonS3->get($file, $localPath);
506 | $this->assertFalse(file_exists($fullpath));
507 | unlink($fullpath); // Bye bye just in case
508 | }
509 |
510 | /**
511 | * testGetBuggyXmlException
512 | *
513 | * @return void
514 | * @author Rob Mcvey
515 | * @expectedException AmazonS3Exception
516 | * @expectedExceptionMessage There was an error communicating with AWS
517 | **/
518 | public function testGetBuggyXmlException() {
519 | $localPath = APP . 'Plugin' . DS . 'AmazonS3' . DS . 'Test' . DS . 'test_app' . DS . 'webroot' . DS . 'files';
520 | $file = 'lolcat.jpg';
521 | $fullpath = $localPath . DS . $file;
522 | $this->AmazonS3->setDate('Mon, 23 Sep 2013 08:46:05 GMT');
523 |
524 | // Mock the built request
525 | $expectedRequest = array(
526 | 'method' => 'GET',
527 | 'uri' => array(
528 | 'host' => 'bucket.s3.amazonaws.com',
529 | 'scheme' => 'https',
530 | 'path' => 'lolcat.jpg'
531 | ),
532 | 'header' => array(
533 | 'Accept' => '*/*',
534 | 'User-Agent' => 'CakePHP',
535 | 'Date' => 'Mon, 23 Sep 2013 08:46:05 GMT',
536 | 'Authorization' => 'AWS foo:+UdJAQGhwJf06KRWWIXumX/5V4Y=',
537 | )
538 | );
539 |
540 |
541 | // Mock the HttpSocket response
542 | $HttpSocketResponse = new stdClass();
543 | $HttpSocketResponse->body = 'Woops
2342134123DFHGDHGDHDFGHfoo';
544 | $HttpSocketResponse->code = 403;
545 | $HttpSocketResponse->headers = array(
546 | 'x-amz-id-2' => '4589328529385938',
547 | 'x-amz-request-id' => 'GSDFGt45egdfsC',
548 | 'Date' => 'Mon, 23 Sep 2013 08:46:05 GMT',
549 | 'Accept-Ranges' => 'bytes',
550 | 'Content-Type' => 'application/xml',
551 | 'Transfer-Encoding' => 'chunked',
552 | 'Connection' => 'close',
553 | 'Server' => 'AmazonS3'
554 | );
555 |
556 | // Mock the HttpSocket class
557 | $this->AmazonS3->HttpSocket = $this->getMock('HttpSocket');
558 | $this->AmazonS3->HttpSocket->expects($this->once())
559 | ->method('request')
560 | ->with()
561 | ->will($this->returnValue($HttpSocketResponse));
562 |
563 | $this->AmazonS3->get($file, $localPath);
564 | $this->assertFalse(file_exists($fullpath));
565 | unlink($fullpath); // Bye bye just in case
566 | }
567 |
568 | /**
569 | * testStringToSignGet
570 | *
571 | * @return void
572 | * @author Rob Mcvey
573 | **/
574 | public function testStringToSignGet() {
575 | $file_path = APP . 'Plugin' . DS . 'AmazonS3' . DS . 'Test' . DS . 'test_app' . DS . 'webroot' . DS . 'files';
576 | $this->AmazonS3->file = 'lolcat.jpg';
577 | $this->AmazonS3->localPath = $file_path;
578 | $this->AmazonS3->setDate('Sun, 22 Sep 2013 14:43:04 GMT');
579 | $expected = "GET" . "\n";
580 | $expected .= "" . "\n";
581 | $expected .= "" . "\n";
582 | $expected .= "Sun, 22 Sep 2013 14:43:04 GMT" . "\n";
583 | $expected .= "/bucket/lolcat.jpg";
584 | $result = $this->AmazonS3->stringToSign('get');
585 | $this->assertEqual($expected, $result);
586 | }
587 |
588 | /**
589 | * testStringToSignPutPng
590 | *
591 | * @return void
592 | * @author Rob Mcvey
593 | **/
594 | public function testStringToSignPutPng() {
595 | $file_path = APP . 'Plugin' . DS . 'AmazonS3' . DS . 'Test' . DS . 'test_app' . DS . 'webroot' . DS . 'files'. DS . 'avatars' . DS . 'copify.png';
596 | $this->AmazonS3->localPath = $file_path;
597 | $this->AmazonS3->file = basename($file_path);
598 | $this->AmazonS3->setDate('Sun, 22 Sep 2013 14:43:04 GMT');
599 | $expected = "PUT" . "\n";
600 | $expected .= "aUIOL+kLNYqj1ZPXnf8+yw==" . "\n";
601 | $expected .= "image/png" . "\n";
602 | $expected .= "Sun, 22 Sep 2013 14:43:04 GMT" . "\n";
603 | $expected .= "/bucket/copify.png";
604 | $result = $this->AmazonS3->stringToSign('put');
605 | $this->assertEqual($expected, $result);
606 | }
607 |
608 | /**
609 | * testStringToSignPutCsv
610 | *
611 | * @return void
612 | * @author Rob Mcvey
613 | **/
614 | public function testStringToSignPutCsv() {
615 | $file_path = APP . 'Plugin' . DS . 'AmazonS3' . DS . 'Test' . DS . 'test_app' . DS . 'webroot' . DS . 'files' . DS . 'dots.csv';
616 | $this->AmazonS3->localPath = $file_path;
617 | $this->AmazonS3->file = basename($file_path);
618 | $this->AmazonS3->setDate('Sun, 22 Sep 2013 14:43:04 GMT');
619 | $expected = "PUT" . "\n";
620 | $expected .= "L0O0L9gz0ed0IKja50GQAA==" . "\n";
621 | $expected .= "text/plain" . "\n";
622 | $expected .= "Sun, 22 Sep 2013 14:43:04 GMT" . "\n";
623 | $expected .= "/bucket/dots.csv";
624 | $result = $this->AmazonS3->stringToSign('put');
625 | $this->assertEqual($expected, $result);
626 | }
627 |
628 | /**
629 | * testStringToSignPutCsv
630 | *
631 | * @return void
632 | * @author Rob Mcvey
633 | **/
634 | public function testStringToSignDeleteCsv() {
635 | $this->AmazonS3->file = 'lolcat.jpg';
636 | $this->AmazonS3->setDate('Sun, 22 Sep 2013 14:43:04 GMT');
637 | $expected = "DELETE" . "\n";
638 | $expected .= "" . "\n";
639 | $expected .= "" . "\n";
640 | $expected .= "Sun, 22 Sep 2013 14:43:04 GMT" . "\n";
641 | $expected .= "/bucket/lolcat.jpg";
642 | $result = $this->AmazonS3->stringToSign('delete');
643 | $this->assertEqual($expected, $result);
644 | }
645 |
646 | /**
647 | * testStringToSignPutPngAdditionalHeaders
648 | *
649 | * @return void
650 | * @author Rob Mcvey
651 | **/
652 | public function testStringToSignPutPngAdditionalHeaders() {
653 | $file_path = APP . 'Plugin' . DS . 'AmazonS3' . DS . 'Test' . DS . 'test_app' . DS . 'webroot' . DS . 'files' . DS . 'avatars' . DS . 'copify.png';
654 | $this->AmazonS3->localPath = $file_path;
655 | $this->AmazonS3->file = basename($file_path);
656 | $this->AmazonS3->setDate('Sun, 22 Sep 2013 14:43:04 GMT');
657 | $this->AmazonS3->amazonHeaders = array(
658 | 'x-amz-acl' => 'public-read',
659 | 'X-Amz-Meta-ReviewedBy' => 'john.doe@yahoo.biz',
660 | );
661 | $expected = "PUT" . "\n";
662 | $expected .= "aUIOL+kLNYqj1ZPXnf8+yw==" . "\n";
663 | $expected .= "image/png" . "\n";
664 | $expected .= "Sun, 22 Sep 2013 14:43:04 GMT";
665 | $expected .= "\n";
666 | $expected .= "x-amz-acl:public-read" . "\n";
667 | $expected .= "x-amz-meta-reviewedby:john.doe@yahoo.biz" . "\n";
668 | $expected .= "/bucket/copify.png";
669 | $result = $this->AmazonS3->stringToSign('put');
670 | $this->assertEqual($expected, $result);
671 | }
672 |
673 | /**
674 | * testBuildAmazonHeaders
675 | *
676 | * @return void
677 | * @author Rob Mcvey
678 | **/
679 | public function testBuildAmazonHeaders() {
680 | $this->AmazonS3->buildAmazonHeaders();
681 | $this->assertTrue(empty($this->AmazonS3->canonicalizedAmzHeaders));
682 | $this->AmazonS3->amazonHeaders = array(
683 | 'x-amz-acl' => 'public-read',
684 | 'X-Amz-Meta-ReviewedBy' => 'john.doe@yahoo.biz',
685 | );
686 | $this->AmazonS3->buildAmazonHeaders();
687 | $this->assertEqual("x-amz-acl:public-read\nx-amz-meta-reviewedby:john.doe@yahoo.biz\n" , $this->AmazonS3->canonicalizedAmzHeaders);
688 | }
689 |
690 | /**
691 | * testSortLexicographically
692 | *
693 | * @return void
694 | * @author Rob Mcvey
695 | **/
696 | public function testSortLexicographically() {
697 | $this->AmazonS3->amazonHeaders = array(
698 | 'X-Amz-Meta-ReviewedBy' => 'bob@gmail.biz',
699 | 'X-Amz-CUSTOM' => 'chicken-soup',
700 | );
701 | $this->AmazonS3->sortLexicographically();
702 | $result = $this->AmazonS3->amazonHeaders;
703 | $expected = array(
704 | 'X-Amz-CUSTOM' => 'chicken-soup',
705 | 'X-Amz-Meta-ReviewedBy' => 'bob@gmail.biz',
706 | );
707 | $this->assertSame($expected, $result);
708 |
709 | //
710 | $this->AmazonS3->amazonHeaders = array(
711 | 'X-Amz-Meta-ReviewedBy' => 'john.doe@yahoo.biz',
712 | 'x-amz-acl' => 'public-read',
713 | );
714 | $this->AmazonS3->sortLexicographically();
715 | $result = $this->AmazonS3->amazonHeaders;
716 | $expected = array(
717 | 'x-amz-acl' => 'public-read',
718 | 'X-Amz-Meta-ReviewedBy' => 'john.doe@yahoo.biz',
719 | );
720 | $this->assertSame($expected, $result);
721 | }
722 |
723 | /**
724 | * testAddAmazonHeadersToRequest
725 | *
726 | * @return void
727 | * @author Rob Mcvey
728 | **/
729 | public function testAddAmazonHeadersToRequest() {
730 | // Request
731 | $request = array(
732 | 'method' => 'PUT',
733 | 'uri' => array(
734 | 'host' => 'bucket.s3.amazonaws.com',
735 | 'scheme' => 'https',
736 | 'path' => 'some/dir/in/the/bucket/dots.csv'
737 | ),
738 | 'header' => array(
739 | 'Accept' => '*/*',
740 | 'User-Agent' => 'CakePHP',
741 | 'Date' => 'Mon, 23 Sep 2013 08:46:05 GMT',
742 | 'Authorization' => 'AWS foo:mCE9RQ8UJYRItzike6XZFd7XjcI=',
743 | 'Content-MD5' => 'L0O0L9gz0ed0IKja50GQAA==',
744 | 'Content-Type' => 'text/plain',
745 | 'Content-Length' => 3
746 | ),
747 | 'body' => '...'
748 | );
749 | $this->AmazonS3->amazonHeaders = array(
750 | 'x-amz-acl' => 'public-read',
751 | 'X-Amz-Meta-ReviewedBy' => 'john.doe@yahoo.biz',
752 | );
753 | $result = $this->AmazonS3->addAmazonHeadersToRequest($request);
754 | // Request
755 | $expected = array(
756 | 'method' => 'PUT',
757 | 'uri' => array(
758 | 'host' => 'bucket.s3.amazonaws.com',
759 | 'scheme' => 'https',
760 | 'path' => 'some/dir/in/the/bucket/dots.csv'
761 | ),
762 | 'header' => array(
763 | 'Accept' => '*/*',
764 | 'User-Agent' => 'CakePHP',
765 | 'Date' => 'Mon, 23 Sep 2013 08:46:05 GMT',
766 | 'Authorization' => 'AWS foo:mCE9RQ8UJYRItzike6XZFd7XjcI=',
767 | 'Content-MD5' => 'L0O0L9gz0ed0IKja50GQAA==',
768 | 'Content-Type' => 'text/plain',
769 | 'Content-Length' => 3,
770 | 'x-amz-acl' => 'public-read',
771 | 'X-Amz-Meta-ReviewedBy' => 'john.doe@yahoo.biz',
772 | ),
773 | 'body' => '...'
774 | );
775 | $this->assertEqual($expected , $result);
776 | }
777 |
778 | /**
779 | * testAddAmazonHeadersToRequestNone
780 | *
781 | * @return void
782 | * @author Rob Mcvey
783 | **/
784 | public function testAddAmazonHeadersToRequestNone() {
785 | // Request
786 | $request = array(
787 | 'method' => 'PUT',
788 | 'uri' => array(
789 | 'host' => 'bucket.s3.amazonaws.com',
790 | 'scheme' => 'https',
791 | 'path' => 'some/dir/in/the/bucket/dots.csv'
792 | ),
793 | 'header' => array(
794 | 'Accept' => '*/*',
795 | 'User-Agent' => 'CakePHP',
796 | 'Date' => 'Mon, 23 Sep 2013 08:46:05 GMT',
797 | 'Authorization' => 'AWS foo:mCE9RQ8UJYRItzike6XZFd7XjcI=',
798 | 'Content-MD5' => 'L0O0L9gz0ed0IKja50GQAA==',
799 | 'Content-Type' => 'text/plain',
800 | 'Content-Length' => 3
801 | ),
802 | 'body' => '...'
803 | );
804 | $result = $this->AmazonS3->addAmazonHeadersToRequest($request);
805 | $this->assertEqual($request , $result);
806 | }
807 |
808 | /**
809 | * testGetContentMd5
810 | *
811 | * @return void
812 | * @author Rob Mcvey
813 | **/
814 | public function testGetContentMd5() {
815 | $file_path = APP . 'Plugin' . DS . 'AmazonS3' . DS . 'Test' . DS . 'test_app' . DS . 'webroot' . DS . 'files' . DS . 'csv_iso8859.csv';
816 | $this->AmazonS3->localPath = $file_path;
817 | $result = $this->AmazonS3->getContentMd5();
818 | $this->assertEqual('ywdK3sOES1D0A+NyHoVKeA==', $result);
819 | // Image
820 | $file_path = APP . 'Plugin' . DS . 'AmazonS3' . DS . 'Test' . DS . 'test_app' . DS . 'webroot' . DS . 'files' . DS . 'avatars' . DS . 'copify.png';
821 | $this->AmazonS3->localPath = $file_path;
822 | $result = $this->AmazonS3->getContentMd5();
823 | $this->assertEqual('aUIOL+kLNYqj1ZPXnf8+yw==', $result);
824 | }
825 |
826 | /**
827 | * testGetLocalFileInfo
828 | *
829 | * @return void
830 | * @author Rob Mcvey
831 | **/
832 | public function testGetLocalFileInfo() {
833 | // CSV
834 | $file_path = APP . 'Plugin' . DS . 'AmazonS3' . DS . 'Test' . DS . 'test_app' . DS . 'webroot' . DS . 'files' . DS . 'csv_iso8859.csv';
835 | $this->AmazonS3->localPath = $file_path;
836 | $result = $this->AmazonS3->getLocalFileInfo();
837 | $this->assertEqual('text/plain' , $result['mime']);
838 | $this->assertEqual(1114 , $result['filesize']);
839 | // Image
840 | $file_path = APP . 'Plugin' . DS . 'AmazonS3' . DS . 'Test' . DS . 'test_app' . DS . 'webroot' . DS . 'files' . DS . 'avatars' . DS . 'copify.png';
841 | $this->AmazonS3->localPath = $file_path;
842 | $result = $this->AmazonS3->getLocalFileInfo();
843 | $this->assertEqual('image/png' , $result['mime']);
844 | $this->assertEqual(48319 , $result['filesize']);
845 | }
846 |
847 | /**
848 | * testPublicUrl
849 | *
850 | * @return void
851 | * @author Rob Mcvey
852 | **/
853 | public function testPublicUrl() {
854 | $this->assertEqual('http://bucket.s3.amazonaws.com/lolcat.jpg' , $this->AmazonS3->publicUrl('lolcat.jpg'));
855 | $this->assertEqual('http://bucket.s3.amazonaws.com/lolcat.jpg' , $this->AmazonS3->publicUrl('/lolcat.jpg'));
856 | $this->assertEqual('http://bucket.s3.amazonaws.com/foo/lolcat.jpg' , $this->AmazonS3->publicUrl('foo/lolcat.jpg'));
857 | $this->assertEqual('http://bucket.s3.amazonaws.com/foo/lolcat.jpg' , $this->AmazonS3->publicUrl('/foo/lolcat.jpg'));
858 | }
859 |
860 | /**
861 | * testSetDate
862 | *
863 | * @return void
864 | * @author Rob Mcvey
865 | **/
866 | public function testSetDate() {
867 | $this->AmazonS3->setDate('Sun, 22 Sep 2013 14:43:04 GMT');
868 | $this->assertEqual('Sun, 22 Sep 2013 14:43:04 GMT', $this->AmazonS3->date);
869 | }
870 |
871 | /**
872 | * testCheckRemoteDir
873 | *
874 | * @return void
875 | * @author Rob Mcvey
876 | **/
877 | public function testCheckRemoteDir() {
878 | $this->AmazonS3->file = 'foo.jpg';
879 | $this->AmazonS3->checkRemoteDir('/flip/flop/bing/bing/toss/');
880 | $this->assertEqual('flip/flop/bing/bing/toss/foo.jpg' , $this->AmazonS3->file);
881 | }
882 |
883 | /**
884 | * testSetDate
885 | *
886 | * @return void
887 | * @author Rob Mcvey
888 | **/
889 | public function testSignature() {
890 | $this->AmazonS3->setDate('Sun, 22 Sep 2013 14:43:04 GMT');
891 | $stringToSign = "PUT" . "\n";
892 | $stringToSign .= "aUIOL+kLNYqj1ZPXnf8+yw==" . "\n";
893 | $stringToSign .= "image/png" . "\n";
894 | $stringToSign .= "Sun, 22 Sep 2013 14:43:04 GMT";
895 | $stringToSign .= "\n";
896 | $stringToSign .= "x-amz-acl:public-read" . "\n";
897 | $stringToSign .= "x-amz-meta-reviewedby:john.doe@yahoo.biz" . "\n";
898 | $stringToSign .= "/bucket/copify.png";
899 | $signature = $this->AmazonS3->signature($stringToSign);
900 | $this->assertEqual('gbcL98O77pVLUSdcSIz4RCAots4=', $signature);
901 |
902 | $this->AmazonS3 = new AmazonS3(array('bang' , 'fizz', 'lulz'));
903 | $signature = $this->AmazonS3->signature($stringToSign);
904 | $this->assertEqual('dF2swNTRWEs9LiMxdxiVeWPwCR0=', $signature);
905 | }
906 |
907 | }
908 |
--------------------------------------------------------------------------------
/Test/test_app/webroot/files/avatars/copify.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/robmcvey/cakephp-amazon-s3/3a580036b4bf4db18c07b8019c50f67002adcab3/Test/test_app/webroot/files/avatars/copify.png
--------------------------------------------------------------------------------
/Test/test_app/webroot/files/csv_iso8859.csv:
--------------------------------------------------------------------------------
1 | "name","words","brief","type","quality","category"
2 | "http://www.corsets4u.co.uk/value-corsets/black-and-white-ruffle-burlesque-corset.html",100,"These corsets are in our ?fashion corset? range, so people usually buy them for fun and dressing up, they?re attracted to the competitive price and the quick delivery time.
3 |
4 | I ? utf-8 and thing's with ""proper"" quotes, yeah?","web page ","standard","lingerie "
5 | "http://www.corsets4u.co.uk/value-corsets/pink-and-black-criss-cross-lace-corset.html",100,"These corsets are in our ?fashion corset? range, so people usually buy them for fun and dressing up, they?re attracted to the competitive price and the quick delivery time.
6 |
7 | I ? utf-8 and thing's with ""proper"" quotes, yeah?","web page ","standard","lingerie "
8 | "http://www.corsets4u.co.uk/value-corsets/white-and-pink-ruffle-burlesque-corset.html",100,"These corsets are in our ?fashion corset? range, so people usually buy them for fun and dressing up, they?re attracted to the competitive price and the quick delivery time.
9 |
10 | I ? utf-8 and thing's with ""proper"" quotes, yeah?","web page ","standard","lingerie "
11 |
--------------------------------------------------------------------------------
/Test/test_app/webroot/files/dots.csv:
--------------------------------------------------------------------------------
1 | ...
--------------------------------------------------------------------------------