Azure Storage に AWS SDK for PHP でアップロードする

Azure Storage に無理やり AWS SDK for PHP でファイルをアップロードしてみました。

Google Cloud Storage には Interoperability API という S3 互換の REST API があるので、それを有効にしさえすれば AWS CLI や AWS SDK をそのまま使うことができます。

- [Google Cloud Storage に AWS CLI や AWS SDK for PHP でアップロード][google-aws]

Azure Storage にはそういうのはないのでそのままだと無理です。

が、以下を見比べてみればほとんど同じなので、リクエストヘッダをちょいちょい弄ればできました（そして AWS SDK for PHP にはそういうのを外部から注入できるようになっていました）。

- [Azure Storage に REST API でアップロードする][azure-rest]
- [Google Cloud Storage に S3 互換の REST でアップロード][google-rest]

----

まず、リクエストの署名のためのインターフェス `SignatureInterface` をインプリメントした `AbstractSignature` を継承して Azure Storage 用のクラスを作ります。

```php:AzureStorageSignature.php
<?php
use Aws\Common\Signature\AbstractSignature;
use Aws\Common\Credentials\CredentialsInterface;
use Guzzle\Http\Message\RequestInterface;

class AzureStorageSignature extends AbstractSignature
{
    private $signableHeaders = array(
        'Content-Encoding',
        'Content-Language',
        'Content-Length',
        'Content-MD5',
        'Content-Type',
        'Date',
        'If-Modified-Since',
        'If-Match',
        'If-None-Match',
        'If-Unmodified-Since',
        'Range',
    );

    public function signRequest(RequestInterface $request, CredentialsInterface $credentials)
    {
        $request->setHeader('Date', gmdate('D, d M Y H:i:s T'));
        $request->setHeader('x-ms-blob-type', 'BlockBlob');
        $request->setHeader('x-ms-version', '2014-02-14');

        $stringToSign = array();
        $stringToSign[] = strtoupper($request->getMethod());

        foreach ($this->signableHeaders as $name) {
            $stringToSign[] = (string)$request->getHeader($name);
        }

        foreach ($this->filterCanonicalizedHeaders($request) as $name => $value) {
            $stringToSign[] = "$name:$value";
        }

        $stringToSign[] = $this->detectCanonicalizedResource($request);
        $signature = $this->signString(implode("\n", $stringToSign), $credentials);
        $account = $credentials->getAccessKeyId();
        $request->setHeader('Authorization', "SharedKey $account:$signature");
    }

    private function signString($string, CredentialsInterface $credentials)
    {
        return base64_encode(
            hash_hmac('sha256', $string, base64_decode($credentials->getSecretKey()), true)
        );
    }

    private function filterCanonicalizedHeaders(RequestInterface $request)
    {
        $headers = [];
        foreach ($request->getHeaders() as $name => $header) {
            $name = strtolower($name);
            if (strpos($name, 'x-ms-') === 0) {
                $value = trim($header);
                if (strlen($value)) {
                    $headers[$name] = $value;
                }
            }
        }
        return $headers;
    }

    private function detectCanonicalizedResource(RequestInterface $request)
    {
        return '/' . explode('.', $request->getHost())[0] . $request->getPath();
    }
}
```

.aws/credentials にストレージアカウントとアクセスキーを保存します。

```
vim ~/.aws/credentials
```

次のように保存します。

```ini
[azure-storage]
aws_access_key_id = YOUR_STORAGE_ACCOUNT
aws_secret_access_key = YOUR_STORAGE_ACCESS_KEY
```

次のように AWS SDK for PHP でアップロードできます。

- エンドポイントには Azure Storage のストレージアカウントを抜かしたエンドポイントを指定
    - `https://oreore.blob.core.windows.net` → `https://blob.core.windows.net`
- `signature` に `AzureStorageSignature` を指定
- バケットにはストレージアカウントを指定
- Key の先頭のセグメントにストレージコンテナを追加

```php
<?php
require __DIR__ . '/vendor/autoload.php';
require __DIR__ . '/AzureStorageSignature.php';

use Aws\S3\S3Client;

$client = S3Client::factory([
    'endpoint' => 'https://blob.core.windows.net',
    'profile' => 'azure-storage',
    'signature' => new AzureStorageSignature(),
]);

$result = $client->putObject([
    'Bucket' => 'YOUR_STORAGE_ACCOUNT',
    'Key'    => 'YOUR_STORAGE_CONTAINER/aws.txt',
    'Body'   => 'aws sdk for php',
]);
```

[google-aws]: http://qiita.com/ngyuki/items/fd3bb47cd4aeb2f61fec "Google Cloud Storage に AWS CLI や AWS SDK for PHP でアップロード"
[azure-rest]: http://qiita.com/ngyuki/items/eb9b890801e49171a0c6 "Azure Storage に REST API でアップロードする"
[google-rest]: http://qiita.com/ngyuki/items/e84e895130de95a5ca7a "Google Cloud Storage に S3 互換の REST でアップロード"