[Oracle Cloud] AWS SDK をつかって、Object Storage にファイルをアップロードしてみた

# はじめに

Oracle Cloud Infrastructure(以下OCI)では、Object Storage を提供しています。Object Storage は、AWS S3 互換のAPIを持っています。そのため、AWS SDK の S3 Client を使用して、OCI の Object Storage へ操作が可能です。
別の表現をすると、AWS SDK を使って S3 の操作をするプログラムを持っているときでも、簡単に OCI Object Storage へ流用することが出来ます。

今回の記事では、AWS SDK を使用して、Object Storage へファイルをアップロードする手順とコードを紹介します。



# Customer Secret Keys を生成

OCI 側で、Customer Secret Keys を生成します。次の記事を参考に設定できます。

[https://qiita.com/sugimount/items/548da947d6ffe61d4e8e#customer-secret-keys-%E7%94%9F%E6%88%90](https://qiita.com/sugimount/items/548da947d6ffe61d4e8e#customer-secret-keys-生成)



# Object Storage の Endpoint 

OCI 上で次の2つの情報を確認すると、Endpoint を把握することが出来ます

- Region Identifier
- Bucket の Namespace



Region Identifier は、次の Document に書かれています

https://docs.cloud.oracle.com/en-us/iaas/Content/General/Concepts/regions.htm

![1584623537839.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/104247/3cbce82c-66aa-a6d8-1b0f-577bfc99af84.png)



Bucket の Namespaceは、OCI Console で詳細を開くと確認できます。

![1584623605964.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/104247/64cd0054-3dce-0104-39d2-1b631e874171.png)



これを、以下の書式にあてはめると、S3互換APIのEndpointがわかります

```
https://<Namespace>.compat.objectstorage.<Region Identifier>.oraclecloud.com
```





# PHP コード for OCI

それでは、実際に AWS SDK をつかって、Object Storage へファイルをアップロードするコードを紹介します。AWS SDK の導入方法だったり、PHP そのものの利用方法は、適当にググって解決します。



S3Client を生成するときのパラメータがポイントです。

- endpoint : Object Storage の S3 互換 API の物を指定します
- signature_version : v4 を指定します
- use_path_style_endpoint : true を指定します
- credentials : OCI で生成した Customer Secret Keys を指定します

```php
<?php

require 'vendor/autoload.php';
use Aws\S3\S3Client;
use Aws\Exception\AwsException;

// OCI
$bucket = 'bucketname';

// // Instantiate the client to OCI
$s3 = new S3Client([
    'version' => 'latest',
    'region'  => 'ap-tokyo-1',
    'endpoint' => 'https://nrfdudkbrtfm.compat.objectstorage.ap-tokyo-1.oraclecloud.com',
    'signature_version' => 'v4',
    'use_path_style_endpoint' => true,
    'credentials' => [
        'key'    => 'your key',
        'secret' => 'your secret',
    ],
]);

// Object Upload
$result = $s3->putObject([
    'Bucket' => $bucket,  // バケット名
    'Key' => 'hello.txt',      // s3のアップロード先
    'Body' => 'hello world!!', // ファイルの内容
]);

echo $result;

?>
```



上記コードを作って、実際に実行したときの出力はこれです。

```shell
> php oci-object-upload.php 
Model Data
----------
Data can be retrieved from the model object using the get() method of the
model (e.g., `$result->get($key)`) or "accessing the result like an
associative array (e.g. `$result['key']`). You can also execute JMESPath
expressions on the result data using the search() method.

{
    "Expiration": "",
    "ETag": "\"570599d420acc25723b337b0db95c7c7\"",
    "ServerSideEncryption": "",
    "VersionId": "",
    "SSECustomerAlgorithm": "",
    "SSECustomerKeyMD5": "",
    "SSEKMSKeyId": "",
    "SSEKMSEncryptionContext": "",
    "RequestCharged": "",
    "@metadata": {
        "statusCode": 200,
        "effectiveUri": "https:\/\/nrfdudkbrtfm.compat.objectstorage.ap-tokyo-1.oraclecloud.com\/awssdktest1\/hello.txt",
        "headers": {
            "date": "Thu, 19 Mar 2020 12:49:09 GMT",
            "content-length": "0",
            "connection": "close",
            "x-amz-request-id": "nrt-1:7r9J0SJr9OC3aovmgRUefrCxJ5lCgh8GmZl-L187kX8_gH0suVizX2ZwB26zuNbN",
            "etag": "\"570599d420acc25723b337b0db95c7c7\"",
            "opc-request-id": "nrt-1:7r9J0SJr9OC3aovmgRUefrCxJ5lCgh8GmZl-L187kX8_gH0suVizX2ZwB26zuNbN",
            "x-api-id": "s3-compatible"
        },
        "transferStats": {
            "http": [
                []
            ]
        }
    },
    "ObjectURL": "https:\/\/nrfdudkbrtfm.compat.objectstorage.ap-tokyo-1.oraclecloud.com\/awssdktest1\/hello.txt"
}
```



実際に、Object Storage にファイルがアップロードされています

![1584624022386.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/104247/075845f3-741e-3d6a-5e23-19307766fd41.png)



# PHP コード for AWS

参考までに、S3 にオブジェクトをアップロードするコードを紹介します。OCI のコードと比較したときに、S3 Client を New するところは違いますが、それ以外のコードはまったく同じことがわかります

```php
<?php

require 'vendor/autoload.php';
use Aws\S3\S3Client;
use Aws\Exception\AwsException;

// AWS
$bucket = 'bucketname';

// Instantiate the client to AWS
$s3 = new S3Client([
    'version' => 'latest',
    'region'  => 'ap-northeast-1',
    'credentials' => [
        'key'    => 'your key',
        'secret' => 'your secret',
    ],
]);

// アップロード
$result = $s3->putObject([
    'Bucket' => $bucket,  // バケット名
    'Key' => 'hello.txt',      // s3のアップロード先
    'Body' => 'hello world!!', // ファイルの内容
]);

echo $result;

?>
```



標準出力

```shell
> php aws-object-upload.php
Model Data
----------
Data can be retrieved from the model object using the get() method of the
model (e.g., `$result->get($key)`) or "accessing the result like an
associative array (e.g. `$result['key']`). You can also execute JMESPath
expressions on the result data using the search() method.

{
    "Expiration": "",
    "ETag": "\"secret\"",
    "ServerSideEncryption": "",
    "VersionId": "",
    "SSECustomerAlgorithm": "",
    "SSECustomerKeyMD5": "",
    "SSEKMSKeyId": "",
    "SSEKMSEncryptionContext": "",
    "RequestCharged": "",
    "@metadata": {
        "statusCode": 200,
        "effectiveUri": "https:\/\/secret.s3.ap-northeast-1.amazonaws.com\/hello.txt",
        "headers": {
            "x-amz-id-2": "Uoc+secret\/APMF6wcbfI+secret=",
            "x-amz-request-id": "secret",
            "date": "Thu, 19 Mar 2020 12:48:42 GMT",
            "etag": "\"secret\"",
            "content-length": "0",
            "server": "AmazonS3",
            "connection": "close"
        },
        "transferStats": {
            "http": [
                []
            ]
        }
    },
    "ObjectURL": "https:\/\/secret.s3.ap-northeast-1.amazonaws.com\/hello.txt"
}
```

# その他 API

## Multiupload

大きいファイルを効率的にアップロードする、Multiupload

ソースコード

```php
<?php

require 'vendor/autoload.php';
use Aws\S3\S3Client;
use Aws\Exception\AwsException;
use Aws\S3\MultipartUploader;
use Aws\Exception\MultipartUploadException;

// OCI
$bucket = 'awssdktest1';

// // Instantiate the client to OCI
$s3 = new S3Client([
    'version' => 'latest',
    'region'  => 'ap-tokyo-1',
    'endpoint' => 'https://idclaqkuq6tu.compat.objectstorage.ap-tokyo-1.oraclecloud.com',
    'signature_version' => 'v4',
    'use_path_style_endpoint' => true,
    'credentials' => [
        'key'    => 'your accesskey',
        'secret' => 'your secretkey',
    ],
]);

// Use multipart upload
$source = '/home/opc/test.txt';
$uploader = new MultipartUploader($s3, $source, [
    'bucket' => $bucket,
    'key' => 'multi1',
]);

try {
    $result = $uploader->upload();
    echo $result;
} catch (MultipartUploadException $e) {
    echo $e->getMessage() . "\n";
}
 

?>
```

result
ObjectURL が https 形式ではない

```
[opc@php1 ~]$ php awssdk.php 
Model Data
----------
Data can be retrieved from the model object using the get() method of the
model (e.g., `$result->get($key)`) or "accessing the result like an
associative array (e.g. `$result['key']`). You can also execute JMESPath
expressions on the result data using the search() method.

{
    "Expiration": "",
    "ServerSideEncryption": "",
    "VersionId": "",
    "SSEKMSKeyId": "",
    "RequestCharged": "",
    "Location": "\/awssdktest1\/multi1",
    "Bucket": "awssdktest1",
    "Key": "multi1",
    "ETag": "\"39b10dd6c6380e65512a189c4d889431-1\"",
    "@metadata": {
        "statusCode": 200,
        "effectiveUri": "https:\/\/idclaqkuq6tu.compat.objectstorage.ap-tokyo-1.oraclecloud.com\/awssdktest1\/multi1?uploadId=e11bd72d-a30a-e79e-b875-a4a9c7f5902d",
        "headers": {
            "date": "Fri, 07 Aug 2020 11:58:48 GMT",
            "content-type": "application\/xml; charset=utf-8",
            "content-length": "339",
            "connection": "keep-alive",
            "x-amz-request-id": "nrt-1:QvTvwAGZIozAODJ8yGBTMV6YS1Kv7oeuCWUtfbTc9AncJiklqQmIs5Y8zmjsieaL",
            "opc-request-id": "nrt-1:QvTvwAGZIozAODJ8yGBTMV6YS1Kv7oeuCWUtfbTc9AncJiklqQmIs5Y8zmjsieaL",
            "x-api-id": "s3-compatible"
        },
        "transferStats": {
            "http": [
                []
            ]
        }
    },
    "ObjectURL": "\/awssdktest1\/multi1"
}
[opc@php1 ~]$ 
```

## Head Object

Object のメタデータを取得

```php

<?php

require 'vendor/autoload.php';
use Aws\S3\S3Client;
use Aws\Exception\AwsException;
use Aws\S3\MultipartUploader;
use Aws\Exception\MultipartUploadException;

// OCI
$bucket = 'awssdktest1';

// // Instantiate the client to OCI
$s3 = new S3Client([
    'version' => 'latest',
    'region'  => 'ap-tokyo-1',
    'endpoint' => 'https://idclaqkuq6tu.compat.objectstorage.ap-tokyo-1.oraclecloud.com',
    'signature_version' => 'v4',
    'use_path_style_endpoint' => true,
    'credentials' => [
        'key'    => 'your accesskey',
        'secret' => 'your secretkey',
    ],
]);

// Object Upload
$result = $s3->headObject([
    'Bucket' => $bucket,  // バケット名
    'Key' => 'multi1',      // s3のアップロード先
]);

echo $result;
 

?>
```

result

```
[opc@php1 ~]$ php headobject.php 
Model Data
----------
Data can be retrieved from the model object using the get() method of the
model (e.g., `$result->get($key)`) or "accessing the result like an
associative array (e.g. `$result['key']`). You can also execute JMESPath
expressions on the result data using the search() method.

{
    "DeleteMarker": false,
    "AcceptRanges": "bytes",
    "Expiration": "",
    "Restore": "",
    "LastModified": "2020-08-07T21:41:37+00:00",
    "ContentLength": 9,
    "ETag": "\"39b10dd6c6380e65512a189c4d889431-1\"",
    "MissingMeta": "",
    "VersionId": "",
    "CacheControl": "",
    "ContentDisposition": "",
    "ContentEncoding": "",
    "ContentLanguage": "",
    "ContentType": "text\/plain",
    "Expires": "1970-01-01T00:00:00+00:00",
    "WebsiteRedirectLocation": "",
    "ServerSideEncryption": "",
    "Metadata": [],
    "SSECustomerAlgorithm": "",
    "SSECustomerKeyMD5": "",
    "SSEKMSKeyId": "",
    "StorageClass": "",
    "RequestCharged": "",
    "ReplicationStatus": "",
    "PartsCount": "",
    "ObjectLockMode": "",
    "ObjectLockRetainUntilDate": "1970-01-01T00:00:00+00:00",
    "ObjectLockLegalHoldStatus": "",
    "@metadata": {
        "statusCode": 200,
        "effectiveUri": "https:\/\/idclaqkuq6tu.compat.objectstorage.ap-tokyo-1.oraclecloud.com\/awssdktest1\/multi1",
        "headers": {
            "date": "Fri, 07 Aug 2020 22:01:49 GMT",
            "content-type": "text\/plain",
            "content-length": "9",
            "connection": "keep-alive",
            "x-amz-request-id": "nrt-1:6DcpvDeFfftBwGNs1mVSjWg7OYjLGVK0z6Wb8T_9zdYtl1BMwO_4Cx7npvvCDGlk",
            "accept-ranges": "bytes",
            "opc-multipart-md5": "ObEN1sY4DmVRKhicTYiUMQ==-1",
            "last-modified": "Fri, 07 Aug 2020 21:41:37 GMT",
            "etag": "\"39b10dd6c6380e65512a189c4d889431-1\"",
            "opc-request-id": "nrt-1:6DcpvDeFfftBwGNs1mVSjWg7OYjLGVK0z6Wb8T_9zdYtl1BMwO_4Cx7npvvCDGlk",
            "x-api-id": "s3-compatible"
        },
        "transferStats": {
            "http": [
                []
            ]
        }
    }
}
[opc@php1 ~]$ 
```

# わかったこと
- Object Storage の Region はどこでもOK。どこの Region でも問題なく操作可能


# 参考URL

Installing the AWS SDK for PHP Version 3
https://docs.aws.amazon.com/sdk-for-php/v3/developer-guide/getting-started_installation.html

Amazon S3 Compatibility API
https://docs.cloud.oracle.com/en-us/iaas/Content/Object/Tasks/s3compatibleapi.htm

Sample Code
https://github.com/awsdocs/aws-doc-sdk-examples/tree/master/php/example_code

AWS SDK
https://docs.aws.amazon.com/sdk-for-php/v3/developer-guide/guide_configuration.html

AWS SDK Referrence
https://docs.aws.amazon.com/aws-sdk-php/v3/api/api-s3-2006-03-01.html#headobject