EC-CUBE の商品画像を Blob Storage で管理する (Azure)


こんにちは。

EC-CUBE では商品画像やダウンロード ファイルなどのバイナリをいくつか扱いますが、今回は、こうした「データ」を「プログラム」(php ファイルや静的コンテンツ) とわけて運用・管理する方法を Azure を使って紹介します。(Azure Blob Storage を使います。)

「データ」と「プログラム」の領域をわけることで、入出力キャッシュやバックアップ等々を分離して最適化・運用できることに加え、さまざまな恩恵が得られます。
例えば、Azure Virtual Machine (仮想マシン) のような IaaS 環境を使って複数インスタンスで運用する場合は、登録されたバイナリを複数インスタンス間で共有する必要がありますが、こうしたケースでも利用できるでしょう。(Azure の場合、Azure File Storage と呼ばれる SMB によるディスク共有が可能なため、この方法を使うこともできます。)

以下では、商品画像を例に、この方法を解説します。

 

Azure Blob Storage の準備

Microsoft Azure では、バイナリ データを管理する専用の Azure Blob Storage と呼ばれるリポジトリーを提供しています。
まずは、Azure Blob Storage を Azure Portal を使って作成します。

Azure Portal (https://portal.azure.com) を起動し、[New] - [Data + storage] - [Storage] で Storage Account を新規作成します。

作成が完了したら、Keys をクリックすることで、Account Name と Key が取得できるので、この内容をコピーしておきましょう。(この後のプログラミングで使用します。)

最後に、[Containers] (上図参照) をクリックして、バイナリー (Blob) の入れ物である Container を新規作成します。(今回は「ecimage」という Container を作成します。)
このあと、CDN を使ったサンプルを紹介するので、今回は、種類として [Blob] (Public Blob) を選択して、登録した Blob データが Anonymous (匿名) で参照できるようにしておきましょう。

 

Azure SDK for PHP の準備

EC-CUBE の商品画像を、作成した Blob Storage に登録するプログラムを記述します。
Azure Blob Storage をはじめとする Azure のサービスの多くは REST を使ってアクセスできますが、PHP から Azure にアクセスする際には PHP 用の Azure SDK を使用すると便利です。(REST をそのまま呼ぶのは、Token の生成など結構大変な作業です。。。) まずは、この SDK を準備します。(以降の手順で、App Service Editor を使って、EC-Cube の入った Azure Web App にそのままセットアップしても構いません。)

今回は Composer でセットアップするため、まず、下記コマンドで Composer をインストールしておきます。
なお、azure_sdk などのサブ フォルダーを作成して、ここにセットアップすると良いでしょう。(pear の設定など、EC-CUBE 側の設定とぶつかってしまう可能性があるので。。。)

curl http://getcomposer.org/installer | php

つぎに、下記の composer.json (テキスト ファイル) を作成します。

{
  "require": {
    "pear-pear.php.net/http_request2": "*",
    "pear-pear.php.net/mail_mime": "*",
    "pear-pear.php.net/mail_mimedecode": "*",
    "microsoft/windowsazure": "*"
  },
  "repositories": [
    {
      "type": "pear",
      "url": "http://pear.php.net"
    }
  ]
}

以下のコマンドを実行して、パッケージをインストールします。(上述の composer.json に従って、Azure SDK がダウンロードされます。)

php composer.phar install

補足 : 上記の通り pear repository を使っていますので、composer ではなく pear を使っても構いません。(pear を使うことで、依存関係の解決など、より簡易にインストールできます。)

 

商品画像の Azure Blob Storage へのアップロード

実際に EC-CUBE 上での商品画像の登録の動きを見ていただくとわかりますが、EC-CUBE では、商品画像は、いったん一時ファイルとしてアップロードをおこなって、商品などの登録確定のタイミングで正式な場所にコピー (公開) されます。
内部では、SC_UploadFile_Ex オブジェクトを使って一時ファイルとして保存し、SC_Image_Ex オブジェクトで一時ファイルから正式な場所にコピーしています。SC_UploadFile_Ex, SC_Image_Ex は、それぞれ SC_UploadFile, SC_Image から派生 (継承) されたオブジェクトで、SC_UploadFile_Ex, SC_Image_Ex には何も実装されていないので、結果的に親クラスの SC_UploadFile, SC_Image が動作しています。

つまり、画像ファイルの保存先をカスタマイズするには、この SC_Image_Ex にプログラムを記述すれば OK です。

下記は、SC_Image_Ex (/data/class_extends/SC_Image_Ex.php) を記述して、画像を Azure Blob に保存するサンプル コードです。(太字が追加したコードです。)
moveTempImage は、既定の動き (SC_Image) では、tmp フォルダーからファイルをコピーしますが、今回は tmp フォルダーの画像ファイルを読み込んで Azure Blob Storage に登録しています。

なお、下記の Account Name、Account Key には、上記で取得した値を設定します。

<?php
require_once CLASS_REALDIR . 'SC_Image.php';
require_once dirname(__FILE__).'\..\..\azure_sdk\vendor\autoload.php';

use WindowsAzure\Common\ServicesBuilder;
use WindowsAzure\Common\ServiceException;
use WindowsAzure\Blob\Models\CreateBlobOptions;

class SC_Image_Ex extends SC_Image
{
  public function moveTempImage($filename, $save_dir)
  {
    $from_path = $this->tmp_dir.$filename;
    $to_path = $save_dir.'/'.$filename;
    if(strcasecmp($save_dir, IMAGE_SAVE_REALDIR) == 0) {
      $con = 'DefaultEndpointsProtocol=http;AccountName=eccubestorage01;AccountKey=B3XbS...';
      $sv = ServicesBuilder::getInstance()->createBlobService($con);
      $content = fopen($from_path, 'r');
      $ext = pathinfo($from_path, PATHINFO_EXTENSION);  
      $opt = new CreateBlobOptions();
      if(strcasecmp($ext, 'gif') == 0) {
        $opt->setBlobContentType('image/gif');
      } else if (strcasecmp($ext, 'jpg') == 0) {
        $opt->setBlobContentType('image/jpeg');
      } else if (strcasecmp($ext, 'jpeg') == 0) {
        $opt->setBlobContentType('image/jpeg');
      } else if (strcasecmp($ext, 'png') == 0) {
        $opt->setBlobContentType('image/png');
      } else if (strcasecmp($ext, 'bmp') == 0) {
        $opt->setBlobContentType('image/bmp');
      } else {
        $opt->setBlobContentType('application/octet-stream');        
      }
      try {
        $sv->createBlockBlob('ecimage', $filename, $content, $opt);
      }
      catch(ServiceException $e){
        $e_code = $e->getCode();
        $e_msg = $e->getMessage();
        GC_Utils_Ex::gfDebugLog('error at azure '.$e_code.': '.$e_msg);
      }
      fclose($content);  
      unlink($from_path);      
    }
  }

  public function deleteImage($filename, $dir)
  {
    // please be implement at deleteImage !
    // (This time, we skip this code...)
    parent::deleteImage($filename, $dir);
  }
}

それほどオーバーヘッドではないと思いますが、もし Blob Storage への再接続が気になるようなら、Blob Service オブジェクトをユーザー キャッシュ (メモリ) に保持して再利用しても良いでしょう。(Azure Web App の PHP 環境には wincache がビルトインされているので、wincache_ucache_add 関数などを使って保持できます。)

なお、今回は moveTempImage のみを実装しましたが、上記の deleteImage でも、同様に、Azure Blob Storage から Blob の削除をおこなうよう実装してください。
また、今回は商品画像のみを対象としていますが、ダウンロード商品用ファイル (DOWN_SAVE_REALDIR, 既定で /data/download/save/ に保存) なども、同様に、Blob で管理すると良いでしょう。

 

商品画像の Azure Blob Storage からの参照

上記で Azure Blob に画像ファイルをアップロードする処理を作成しましたが、今度は逆に、商品画像として Azure Blob Storage のファイルを参照する一例を紹介します。
上記と同様に、例えば、画像ファイルの URL を作成している箇所 (コード) を拡張クラスで書き換える方法もありますが、今回は他への影響が少ない方法として URL Rewrite を使用してみます。

EC-CUBE では、商品画像の参照先として、IMAGE_SAVE_URLPATH パラメーターの値 (既定値は /upload/save_image) を参照します。
そこで、商品画像として Azure Blob Storage を参照させるために、この URL にアクセスされた際に、カスタムのプログラムで処理するよう、別の URI に書き換えます。例えば、http://.../upload/save_image/nabe.jpg を http://.../viewblob.php?filename=nabe.jpg に Rewrite して、この viewblob.php で Azure Blob Storage を参照するようプログラミングします。

まず、この URL の書き換えには、Web.config を使用します。(Apache の場合は、.htaccess を使用します。)
例えば、Web.config に下記の通り追記すると、/upload/save_image/<file name> を参照した際に、/viewblob.php?filename=<file name> に Rewrite されます。

<configuration>
  <system.webServer>
    . . .

    <rewrite>
      <rules>
        <rule name="ViewBlob" stopProcessing="true">
          <match url="^upload/save_image/(.+)$" ignoreCase="true" />
          <action type="Rewrite" url="viewblob.php?filename={R:1}" appendQueryString="false" />
        </rule>
      </rules>
    </rewrite>
  </system.webServer>
</configuration>

そして、viewblob.php を新規作成して下記の通り記述することで、Blob の画像ファイルを取得してブラウザーに出力します。
実際の開発では、Client Cache の使用 (ヘッダー挿入) など、効率性を考慮してプログラミングしてください。(今回は、これらの処理は省いてます。)

<?php
require_once './require.php';
require_once dirname(__FILE__).'\azure_sdk\vendor\autoload.php';

use WindowsAzure\Common\ServicesBuilder;
use WindowsAzure\Common\ServiceException;
use WindowsAzure\Blob\Models\CreateBlobOptions;

$filename = $_GET['filename'];

$con = 'DefaultEndpointsProtocol=http;AccountName=eccubestorage;AccountKey=B3XbS...';
$sv = ServicesBuilder::getInstance()->createBlobService($con);
$blob = $sv->getBlob('ecimage', $filename);
$props = $blob->getProperties();
$ctype = $props->getContentType();
header('Content-Type: '.$ctype);
// please set other headers ! (client cache, etc)
fpassthru($blob->getContentStream());
?>

なお、登録、参照共に、/azureblob/save_image のような架空の Path を設定して、「/azureblob で始まる URL なら Azure Blob Storage を見に行く」などの処理で統一しておくと、画像ファイル以外にも拡張しやすくなるでしょう。

 

Azure CDN の使用

Blob 参照のもう 1 つの方法として、Azure CDN (Contents Delivery Network) を使用する方法もあります。

静的な画像データなどは、Azure CDN を使用することで、地理的に近いサーバー (Edge Server) を使ったダウンロードや、Server Cache による高速化など、効率的なダウンロードが可能です。(EC-CUBE の場合はあまりないかもしれませんが、海外展開 (越境 EC) などの場合は、Azure CDN や Azure Traffic Manager を使うと効果大です。)
また、画像ファイルの取得を EC-CUBE の Server (プログラム本体) と分離して提供できるので、サーバーの負荷軽減にも寄与できるでしょう。

Azure CDN の使用は簡単です。
例えば、上記の Azure Blob Storage に保存された画像ファイルを CDN で配信してみましょう。

まず、Azure Portal で [New] - [Media + CDN] - [CDN] を選択して CDN を作成します。
作成の際、Original Domain として、上記で作成した Azure Blob Storage (上記の eccubestorage01) を選択します。

補足 : 2015 年 03 月現在、まだ CDN は New Portal では作成できないので、https://manage.windowsazure.com を使って作成してください。

同期が完了するまで、しばらく待ってください。以下にアクセスして画像が表示されれば完了です。(一度反映されたら、以降は、商品画像を Azure Blob Storage に登録すると、すぐに CDN から参照できます。)

http://<cdn namespace>.vo.msecnd.net/<container name>/<file name>

補足 : 上述の通り、作成する Azure Storage の Container は、あらかじめ Public Blob に設定しておいてください。

あとは、上記同様、Web.config を記述して「/upload/save_image にアクセスした場合、http://<cdn namespace>.vo.msecnd.net/ecimage を参照する」ように設定します。(Apache の場合は、.htaccess に設定を記述します。)

<configuration>
  <system.webServer>
    . . .

    <rewrite>
      <rules>
        <rule name="CdnBlob" stopProcessing="true">
          <match url="^upload/save_image/(.+)$" ignoreCase="true" />
          <action type="Redirect" url="http://<cdn namespace>.vo.msecnd.net/ecimage/{R:1}" appendQueryString="true" />
        </rule>
      </rules>
    </rewrite>
  </system.webServer>
</configuration>

補足 : 今回、上記の通り (URL Rewrite ではなく) URL Redirect を使用している点に注意してください。このため、初回のアクセス時に多少のオーバーヘッド (ネットワーク round-trip) が発生してしまう点に注意してください。
EC-CUBE の IMAGE_SAVE_URLPATH パラメーターで別ドメイン (http://<cdn namespace>.vo.msecnd.net) が指定できないため、このような別ドメイン参照の Web.config 設定が必要です。

補足 : 現在、Azure File Storage に登録されたファイルは Azure CDN で公開できないので注意してください。(CDN が使えるのは、上述の Azure Blob Storage か、Azure Web App か、Azure Media Services のデータのみが対象です。)

上述の通り、CDN では効率的に静的ファイルを取得します。ブラウザーに付いている Profiler (開発ツール) などを使って画像ファイルの取得速度を確認していただくと、その効果が確認していただけます。

 

※参考情報「EC-CUBE の簡単インストールと、実運用のための構成変更

 

Comments (0)

Skip to main content