9
votes

If I try to get an object from my S3 bucket that doesn't exist, the Amazon PHP SDK 2 gives me a pretty ugly error. Handy for me but means nothing to the end user...

E.g:

$s3 = $aws->get('s3');

$result = $s3->getObject(array(
    'Bucket' => 'my bucket',
    'Key'    => 'path/to/file'
));

The error:

Fatal error: Uncaught Aws\S3\Exception\NoSuchKeyException: AWS Error Code: NoSuchKey, Status Code: 404, AWS Request ID: xxxxxxxxxxxxx, AWS Error Type: client, AWS Error Message: The specified key does not exist. thrown in AWS/vendor/aws/aws-sdk-php/src/Aws/Common/Exception/NamespaceExceptionFactory.php on line 89

Is there a way that I can determine if there is an error and print a message that makes sense rather than the above?

3
If you enable the web page on S3, there is an option for you to redirect File Not Found error to a page (in my case, I use a jpg file that says "File Not Found", so this would work for images too)Alvin K.
Thanks @Alvin K. Not sure how that would work for me as I have multiple file types so returning a JPG file wouldn't really help me. But thanks anyway.Ben Sinclair
Since the jpg file is fixed, a md5 checksum will identify which file is returned and you "catch" this 404 error.Alvin K.

3 Answers

9
votes

It suddenly occurred to me to try this:

try {
    $result = $s3->getObject(array(
        'Bucket' => 'my bucket',
        'Key'    => 'path/to/file'
    ));
} catch (Exception $e) {
   // I can put a nicer error message here  
}
6
votes

All errors that occur in calls to methods of the AWS SDK are indicated by throwing exceptions. You can catch those exceptions if you want to handle the errors.

In the simplest case, you might just want to catch Exception:

try {
    $result = $s3->getObject(array(
        'Bucket' => 'my bucket',
        'Key'    => 'path/to/file'
    ));
}
catch (Exception $e) {
    echo 'Oops, something went wrong';
}

If you only want to handle certain expected exceptions, though, while letting others bubble up and crash your application, then things get a little more subtle.

Firstly, each of the few dozen namespaces within the AWS namespace contains an Exception namespace in which it defines exception classes. One of these classes in each namespace is what Amazon calls the default service exception class for the namespace, from which all other exceptions inherit.

For example, S3 has the Aws\S3\Exception namespace and the S3Exception class. EC2 has the Aws\Ec2\Exception namespace and the Ec2Exception class.

Note that catching one of these exceptions instead of the base Exception class immediately stops us catching certain errors! The service-specific exceptions are thrown as a result of error responses from the server; connection failure exceptions do not inherit from them. For example, if you try running the following code without an internet connection...

try {
    $result = $s3->getObject(array(
        'Bucket' => 'my bucket',
        'Key'    => 'path/to/file'
    ));
}
catch (S3Exception $e) {
    echo 'Oops, something went wrong';
}

... then the exception will not be caught (since it will be a Guzzle\Http\Exception\CurlException, not an S3Exception) and the program will crash. For this reason, if you're catching these exceptions just to provide generic failure messages to the user, you should probably catch Exception.

Let's return to the question of how to handle a specific error. For most of the namespaces, the answer is that there will be an exception class defined for that error, and you should catch that. For example, let's say we're again using the S3 getObject method and want to do something when the bucket we ask for doesn't exist. Looking in the S3 Exception namespace docs, we see that there is a NoSuchBucketException we can catch:

try {
    $result = $s3->getObject(array(
        'Bucket' => 'my bucket',
        'Key'    => 'path/to/file'
    ));
}
catch (NoSuchBucketException $e) {
    echo 'There is no such bucket.';
}

(In practice, it may well be easier to figure out which exceptions can be thrown by what operations through trial and error than through carefully reading the docs.)

Finally, it is worth mentioning the EC2 API. Unlike all the other services, the EC2 namespace includes only a single exception class, the Ec2Exception. If you want to catch and handle a specific error, you need to inspect the exception object to figure out what kind of error you're dealing with. You can do this by checking the value returned by the getExceptionCode() method of the exception.

For example, a (modified) snippet from a script I recently wrote that grants specified IPs access to our MySQL server:

try {
    $result = $ec2->authorizeSecurityGroupIngress([
        'GroupName' => 'mygroup',
        'IpProtocol' => 'tcp',
        'ToPort' => 3306,
        'CidrIp' => $ip . "/32",
    ]);
}
catch (Ec2Exception $e) {
    if ($e->getExceptionCode() == 'InvalidPermission.Duplicate') {
        echo "IP already has requested permission.";
    }
    else {
        // Don't know how to deal with this error; let's crash
        throw $e;
    }
}

Note that the possible exception codes - like InvalidPermission.Duplicate in this case - are not listed in the AWS PHP SDK documentation, but you can find them by trial and error or from the documentation for the EC2 API itself, in which each API action's page contains an 'Errors' section listing the error codes it can return.

2
votes

You can also use this method: $response = $s3->doesObjectExist( $bucket, $key );

It will return a boolean true response if the object exists.

AWS Docs for doesObjectExist