Azure Storage SAS access 延長戦【主にREST API sample code】

基本的なことは以下の記事にて御確認ください。
> Azure Storageへのアクセス方法 【Shared Access Signatures (SAS)】
> http://qiita.com/snomoto/items/c2793866dee81b4718e9

# 本記事の概要
SASを利用した、Azure Storageに対してアップロード、ダウンロード操作をするためのサンプルコードをメインに記載します。SDKは使ってません。
動作確認したバージョンは**"Azure Storage version 2015-12-11"**ですので、お間違いなく。ここが違うだけで動きませんのでお気をつけ下さい。（動くこともあるが、動かないと思った方が良い。。。）

# PHP Sample
前回の記事で、BLOBの中身を参照するためのサンプルを記載しました。そちらを編集して、サンプルコードを作成していきます。
cURLについてはコチラ。
http://php.net/manual/ja/book.curl.php

## コンテナー内のBLOBをリストアップする

```php:listingBLOB.php
<?php

/**
 *  For Azure Storage Version 2015-12-11. Listing BLOB.
 *  @author snomoto
*/

$key = 'copy&paste Your Access KEY';

$azureStorageAccount = "your storage account";
$azureContainerID    = "your container ID";

$azureURL = "https://".$azureStorageAccount.".blob.core.windows.net/".$azureContainerID."?";

$sasEnd = "2016-12-03T19:43:58Z"; //sample
$sasIP  = "your IP address";

$sigStr20151211 =        "rwl"."\n". //permission
                         ""."\n".    //start
                         $sasEnd."\n".    //end
                         "/blob/".$azureStorageAccount."/".$azureContainerID."\n". //canonicalizedresource
                         ""."\n".    //identifier
                         $sasIP."\n".   //IP address
                         "https,http"."\n".     //protocol
                         "2015-12-11"."\n".     //VERSION
                         ""."\n".    //rscc
                         ""."\n".    //rscd
                         ""."\n".    //rsce
                         ""."\n".    //rscl
                         "";         //rsct

$sig20151211 = base64_encode(
     hash_hmac('sha256', urldecode(utf8_encode($sigStr20151211)), base64_decode($key), true)
     );

//echo urlencode($sig20151211)."\n";

$URI = $azureURL."se=".urlencode($sasEnd)."&sr=c&sp=rwl&sip=".$sasIP."&spr=https,http&sig=".urlencode($sig20151211)."&sv=2015-12-11&comp=list&restype=container";

echo $URI."\n";
```
上記のlistingBLOB.phpの一番下に、以下の3行を付け足します。
IPアドレスをサーバのものに書き換えるのを忘れないでください。

```php:listingBLOBapi.php

/**
 *   copy&paste listingBLOB.php.
 *   Don't forget to replace IP address with your "Server's" IP Address.
*/

$curl = curl_init($URI);
$result = curl_exec($curl);
echo $result."\n";

```
## コンテナーからBLOBデータをダウンロードする

急に難しそうなドキュメントになります。
> https://docs.microsoft.com/en-us/rest/api/storageservices/fileservices/operations-on-blobs

意味わからない事が多かったのですが、MS社員の方のブログを参照し、何とか動くものが出来ました。（感謝！）
> https://blogs.msdn.microsoft.com/tsmatsuz/2016/07/06/how-to-get-azure-storage-rest-api-authorization-header/

というか、上記の公式ドキュメントの通りに辿っていきヘッダーを設定すると動きませんでした。
> https://docs.microsoft.com/en-us/rest/api/storageservices/fileservices/authentication-for-the-azure-storage-services#a-namesubheading2a-specifying-the-authorization-header

```scala
//公式のauthorizationの説明だけがイコール表現になっている。
Authorization="[SharedKey|SharedKeyLite] <AccountName>:<Signature>"

//他の資料はコロン。
Authorization: [SharedKey|SharedKeyLite] <AccountName>:<Signature>
            ^^^      

//もしくは配列を意識するなら、こういう書き方？
headers['Authorization'] = 'SharedKey <AccountName>:<Signature>'
```
何のことはないのですが、公式ドキュメント通りにイコールを設定してしまいハマってしまいました。

改めてsample codeを書いてみます。

```php:DownloadBLOBapi.php
<?php
/**
 *  For Azure Storage Version 2015-12-11. Downloading BLOB data.
*/

$AzureStorageVersion = "2015-12-11";

$key = 'copy&paste Your Access KEY';

$azureStorageAccount = "your storage account";
$azureContainerID    = "your storage account";
$azureBlobData       = "moviedata.json";

$azureURL = "https://".$azureStorageAccount.".blob.core.windows.net/".$azureContainerID."/".$azureBlobData;

$x_ms_date = gmdate('D, d M Y H:i:s T', strtotime('+1 week'));

$sigStr20151211 = "GET\n". //VERB
                  "\n".    //Encoding
                  "\n".    //Lang
                  "\n".    //Length
                  "\n".    //MD5
                  "\n".    //Type
                  "\n".    //Date
                  "\n".    //IMS
                  "\n".    //IM
                  "\n".    //INM
                  "\n".    //IUS
                  "\n".    //Range
                  "x-ms-date:".$x_ms_date."\n".
                  "x-ms-version:".$AzureStorageVersion."\n".
                  "/".$azureStorageAccount."/".$azureContainerID."/".$azureBlobData;

$sig20151211 = base64_encode(hash_hmac('sha256', $sigStr20151211, base64_decode($key), true));

$headers = array(
    "x-ms-version:".$AzureStorageVersion,
    "x-ms-date:".$x_ms_date,
    "HOST:".$azureStorageAccount.".blob.core.windows.net",
    "Authorization:SharedKey ".$azureStorageAccount.":".$sig20151211
     );


$curl = curl_init();
curl_setopt($curl, CURLOPT_URL, $azureURL);
curl_setopt($curl, CURLOPT_HTTPHEADER, $headers);

$result = curl_exec($curl);
echo $result."\n";

//外部パートナーにはURIと、このHEADERの値を教えてあげれば良い・・・？
print_r($headers);
```


## コンテナーにファイルをアップロードする

改めてjsonファイルを作成してみます。
元ネタはこちら。http://ir.yahoo.co.jp/en/company/profile.html
（TGT紀尾井町にはコワーキングスペースがあり、2016年12月06日時点では無料で利用できます！https://lodge.yahoo.co.jp/  ）

```xml:terminal
sudo vi companyinfo.json
```

```json:companyinfo.json
{
    "Company Name": "Yahoo Japan Corporation",
    "Businesses": [
        "Internet Advertising business",
        "e-Commerce business",
        "Members Services business",
        "Other businesses"
    ],
    "Headquarters": "Kioi Tower, 1-3 Kioicho, Chiyoda-ku, Tokyo, 102-8282, Japan",
    "Industrial Classification": "Information & Communication",
    "Business Term Ends": "March 31",
    "Stock Trading Unit": "100",
    "Founded": "January 31, 1996",
    "IPO Date": "November 4, 1997 (JASDAQ)"
}
```

dateの箇所でガッツリとハマっていたのですがコチラの記事を読み解決しました。（感謝！）
http://qiita.com/ngyuki/items/eb9b890801e49171a0c6

ポイントは「x-ms-dateでは動かない」ところでした。。。以下がSample Codeです。

```php:uploadBLOB.php
<?php

$AzureStorageVersion = "2015-12-11";


$key = 'copy&paste Your Access KEY';

$azureStorageAccount = "your storage account";
$azureContainerID    = "your container name";
$azureNewBlobData    = "uploadCompanyInfo.json";
$azureURL = "https://".$azureStorageAccount.".blob.core.windows.net/".$azureContainerID."/".$azureNewBlobData;

$body = file_get_contents("companyinfo.json");

$Content_Type = "application/json";

$x_ms_date = gmdate('D, d M Y H:i:s T', strtotime('+1 week'));

$sigStr20151211 = "PUT\n". //VERB
                "\n".    //Encoding
                "\n".    //Lang
                strlen($body)."\n". //Length
                "\n".    //MD5
                $Content_Type."\n". //Type
                $x_ms_date."\n".    //Date
                "\n".    //IMS
                "\n".    //IM
                "\n".    //INM
                "\n".    //IUS
                "\n".    //Range
                "x-ms-blob-type:BlockBlob\n".
                "x-ms-version:".$AzureStorageVersion."\n".
                "/".$azureStorageAccount."/".$azureContainerID."/".$azureNewBlobData;

$sig20151211 = base64_encode(hash_hmac('sha256', $sigStr20151211, base64_decode($key), true));

$headers = array(
    "x-ms-version:".$AzureStorageVersion,
    "Date:".$x_ms_date,
    "x-ms-blob-type:BlockBlob",
    "Content-Type:".$Content_Type,
    "Content-Length:".strlen($body),
    "Authorization:SharedKey ".$azureStorageAccount.":".$sig20151211
);

$curl = curl_init();
curl_setopt($curl, CURLOPT_CUSTOMREQUEST, "PUT");
curl_setopt($curl, CURLOPT_URL, $azureURL);
curl_setopt($curl, CURLOPT_HTTPHEADER, $headers);
curl_setopt($curl, CURLOPT_POSTFIELDS,$body);

$result = curl_exec($curl);

//外部パートナーにはstrlen($body)の部分を教えてもらい、お返しにURIとHEADERの値を教えてあげれば良い・・・？
print_r($headers);
```

かなりの時間を費やしてしまったので、改めて記載しますが、公式資料における下図スクリーンショット部分の表現、Date or x-ms-dateとあるのですが、x-ms-dateの方を使うと動きませんでした。。(2016年12月06日時点)
>https://docs.microsoft.com/en-us/rest/api/storageservices/fileservices/put-blob
<img width="1181" alt="スクリーンショット 2016-12-06 19.12.28.png" src="https://qiita-image-store.s3.amazonaws.com/0/147747/68b111ed-d624-066d-7c95-c1f32e7a9264.png">

Date Requiredです。。


※2016年12月追記です
上記のやり方で64MB以上のデータをアップロードしようとすると、エラーとなります。4MBずつ区切るなどテクニックがいるのですが、SDKを利用すれば意識せずアップロードできます。



# おわりに
他にも色々な操作があるようですが、資料が分かり辛いのでここまでとします。。
SDKを使った方が簡単かも、Azure Storage Explorerを使った方が超簡単だと思います。

他に、別記事にてAzure API Managementを使ったIPアドレスによるアクセス制御の方法を記載したので、更なるセキュアアクセス環境を求める方はそちらを利用してみてはどうでしょうか。
> Azure API Management【APi Gateway】 ip-filter
> http://qiita.com/snomoto/items/aa62cbfc0de136391995

気が向いたら、他の言語のサンプルコードを書き足します。