RMS APIは楽天市場の店舗管理、商品管理の自動化を実現するAPIです。あまりサンプルがまとまっていないので、APIの全パターンを下記記事でまとめています。
楽天 RMS APIのPHPサンプル集・使い方 ほぼ全パターン(随時更新中)
今回は画像登録関連のR-CabinetAPI(CabinetAPI)、cabinet.file.insertを解説します。
繰り返しになりますがAPIを使用できる環境がない場合は、下記から出店申請をしてください。たけーってなった場合は既に店舗アカウントを持っている人に頼んでテスト店舗を使用させてもらうのも手です。
サンプルソースコードは下記です。
cabinet.file.insertのサンプルソースコード
cabinet.file.insertの概要
cabinet.file.insertのRESTのエンドポイントにMultipartでバイナリをPOSTすると、RMS上に画像を新規登録できます。
登録できる設定項目例は下記です。
- 画像の名前やパス
- アップするR-Cabinetのフォルダ
- 上書きするか否か
- 画像のバイナリ (Multipart部分)
マルチパートでバイナリを送信する部分が厄介なので詳しく説明していきます。
POST リクエスト例
バイナリの部分は省略していますがこんな感じです。
Connection: keep-alive
Proxy-Connection: keep-alive
Content-Type: multipart/form-data; boundary="a0b75a1f4b3cf55958d6263a61390ccd"
Content-Length: 154375
Authorization: ESA base64エンコードしたサービスシークレットとライセンスキー
Accept: */*"
--a0b75a1f4b3cf55958d6263a61390ccd
Content-Disposition: form-data; name="xml"
<?xml version="1.0" encoding="UTF-8"?>
<request>
<fileInsertRequest>
<file>
<fileName>test_siz_20180702125554</fileName>
<folderId>0</folderId>
<filePath>test_image.jpg</filePath>
<overWrite>true</overWrite>
</file>
</fileInsertRequest>
</request>
--a0b75a1f4b3cf55958d6263a61390ccd
Content-Disposition: form-data; name="file"; filename="test_image.jpg"
Content-Type: image/jpeg
バイナリ
--a0b75a1f4b3cf55958d6263a61390ccd--
リクエストheaderにbase64エンコードしたサービスシークレットとライセンスキーを付与してPOSTして認証を行います。
また、リクエストheaderにmultipartのバウンダリー文字列を入れ、その後の各リクエストの間に挿入していきます。こんな感じ。
--バウンダリ文字列 ファイル名とかフォルダの設定とか上書き設定などのリクエスト nameはxml --バウンダリ文字列 マルチパートによる画像ファイルのバイナリを記述 nameはfile --バウンダリ文字列--
headerのContent-Lengthにはこのリクエスト全部のサイズがバイト数で入ります。めんどーい!
ソースコード解説
要は前述のxmlを作ってPOSTできればいいわけです。やることは下記。
- POSTリクエストする画像ファイルクラスオブジェクト、画像設定のクラスオブジェクト作成し、設定する
- 画像設定のクラスオブジェクトをxmlに変換
- 送信したい画像ファイルオブジェクトをバイナリに変換してxmlに追記
- file_get_contentsでPOST
です。
POSTリクエストする画像ファイルクラスオブジェクト、画像設定のクラスオブジェクトを作成して設定
まずは送信する画像ファイルの情報を格納する、画像ファイルクラスオブジェクトを作成します。
class/cabinetUploadFileInfo.php
<?php
class CabinetUploadFileInfo
{
public $filePath;
public $mimeType;
// public $pathInfo; //Array([dirname] => ./hoge,[basename] => hoge.jpg,[extension] => jpg,[filename] => hoge)
public $extension; // 拡張子
function __construct($filePath) {
$this->filePath = $filePath;
$this->mimeType = mime_content_type($this->filePath); // image/jpegみたいなやつ
$extensionArrayDef = array(
'gif' => 'image/gif',
'jpg' => 'image/jpeg',
'png' => 'image/png',
'tiff' => 'image/tiff',
'bmp' => 'image/bmp',
);
//MIMEタイプから拡張子を決定
$this->extension = array_search($this->mimeType, $extensionArrayDef,true);
}
}
コンストラクタでファイルのパスを渡すと、内部でそのファイルパスとMIME-TYPE、拡張子を判別して保持するようにします。後のMultiPartによる送信部分で使います。
クラスが作れたらとりあえずセット。
insertCabinetFile.php
// 送信したいファイルの情報設定 $cabinetUploadFileInfo = new CabinetUploadFileInfo(__DIR__ . '/image/test_image.jpg'); // 送信したいファイルの絶対パスを指定 mimeとかいろんな情報をconstructerでメンバーに設定している
次に画像設定のクラスオブジェクトを作成します。R-Cabinet上での登録名やどのフォルダにアップするかを記述する部分です。
<?php
class CabinetFileSetting
{
public $fileName; //登録画像名 50バイト以内。その他制約あり。要マニュアル確認
public $folderId; //登録先フォルダID。 0だと基本フォルダ。その他はcabinet.folders.get APIで調べられる
public $filePath; //登録file名 20バイト以内。その他制約あり。要マニュアル確認
public $overWrite; //overWriteがtrueかつfilePathの指定がある場合、filePathをキーとして画像情報を上書きすることができます。
function __construct() {
}
}
$folderIdを0に設定するとR-Cabinetのルートにある「基本フォルダ」配下にアップされます。基本フォルダ配下はフォルダが作成できないため、自由度を高めるのであれば作成した別のフォルダを指定することをオススメします。ちなみにフォルダの作成も、cabinet.folder.insert APIを使って自由に作成できます。
cabinet.folder.insert APIの使い方はこちら(準備中)
$folderIdはフォルダを作成する度に割り当てられる一意な数値です。フォルダIDはcabinet.folders.get APIで取得できます。
cabinet.folders.get APIの使い方はこちら(準備中)
特定のフォルダ配下にアップしたければ、cabinet.folders.get APIでフォルダIDを調べた上でアップするという流れになります。
クラスを作ったら、画像設定の値を入れていき、POSTする関数cabinetFileInsertにぶち込みます。
insertCabinetFile.php
// 送信したいファイルの情報設定
$cabinetUploadFileInfo = new CabinetUploadFileInfo(__DIR__ . '/image/test_image.jpg'); // 送信したいファイルの絶対パスを指定 mimeとかいろんな情報をconstructerでメンバーに設定している
// 画像の名前やパスなど
$cabinetFileSetting = new CabinetFileSetting();
$cabinetFileSetting->fileName = 'test_' . randomStr(3) . '_' . date_format(new DateTime('now', new DateTimeZone('Asia/Tokyo')), 'YmdHis');
$cabinetFileSetting->folderId = 0; // 0は基本フォルダ folderIdはcabinet.folders.getで確認可能
$cabinetFileSetting->filePath = basename($cabinetUploadFileInfo->filePath); // 拡張子つける 送信する実ファイルの名前を正確に「hoge.jpg」とか
$cabinetFileSetting->overWrite = "true"; // 文字列指定でなければならない「true」とか「false」とか。overWriteがtrueかつfilePathの指定がある場合、filePathをキーとして画像情報を上書きすることができます
// 楽天へRMS APIを使って画像アップロード
list($reqXml, $httpStatusCode, $response) = cabinetFileInsert($cabinetFileSetting, $cabinetUploadFileInfo);
画像設定のクラスオブジェクトをxmlに変換
item.insertの回で使ったのと同じのを使います。
やることとしては、
- クラスオブジェクトを一旦配列に変換
- リクエストのXMLをarray情報から作成する
- arrayをforeachで回し、keyとvalueを元にSimpleXMLElementに追加していく
前述のCabinetFileSettingのオブジェクトをxmlの下記の部分に変換していく作業です。
<?xml version="1.0" encoding="UTF-8"?>
<request>
<fileInsertRequest>
<file>
<fileName>test_siz_20180702125554</fileName>
<folderId>0</folderId>
<filePath>test_image.jpg</filePath>
<overWrite>true</overWrite>
</file>
</fileInsertRequest>
</request>
実装としてはitem.insertの時とほぼ同じです。
function cabinetFileInsert($cabinetFileSetting, $cabinetUploadFileInfo) {
$authkey = base64_encode(RMS_SERVICE_SECRET . ':' . RMS_LICENSE_KEY);
$url = RMS_API_CABINET_FILE_INSERT;
$reqXml = _createRequestXml($cabinetFileSetting);
。。。省略
}
/*
* 渡したclassオブジェクトからリクエストのXMLを自動生成する
*/
function _createRequestXml($cabinetFile) {
// リクエストXMLのガワを作る
$rootXml = new SimpleXMLElement('<?xml version="1.0" encoding="UTF-8"?><request/>');
$fileInsertRequestXml = $rootXml->addChild('fileInsertRequest');
$fileXml = $fileInsertRequestXml->addChild('file');
// 受け取った商品情報オブジェクトをarrayに変換
$array = _convertClassObjectToArray($cabinetFile);
_arrayToXml($array, $fileXml); // リクエストのXMLをarray情報から作成する
return $rootXml->asXML(); // リクエストのXMLを返却する
}
/**
* Convert an array to XML
* @param array $array
* @param SimpleXMLElement $xml
* @param array $parentKeyName (その要素が配列で、子要素を親要素の単数形にして登録したい時指定)
*/
function _arrayToXml($array, &$xml, $parentKeyName=null){
foreach ($array as $key => $value) {
if(is_array($value)){
if(is_int($key)){
if(!empty($parentKeyName)) {
// 親要素が存在する時、子要素を親要素の単数形の名前にして登録
$key = singularByPlural($parentKeyName);
}
}
$label = $xml->addChild($key);
_arrayToXml($value, $label, $key);
}
else if(!is_null($value)){
// 値がセットされている時だけxml要素に追加
$xml->addChild($key, $value);
}
}
}
/**
* Convert an classObject to array
*/
function _convertClassObjectToArray($object) {
$json = json_encode($object);
return (array)json_decode($json, true);
}
これで$reqXmlには下記のxmlの文字列が入りました。
<?xml version="1.0" encoding="UTF-8"?>
<request>
<fileInsertRequest>
<file>
<fileName>test_siz_20180702125554</fileName>
<folderId>0</folderId>
<filePath>test_image.jpg</filePath>
<overWrite>true</overWrite>
</file>
</fileInsertRequest>
</request>
送信したい画像ファイルオブジェクトをバイナリに変換してxmlに追記
次はいよいよ下記のxml部分を作ります。
Connection: keep-alive Proxy-Connection: keep-alive Content-Type: multipart/form-data; boundary="a0b75a1f4b3cf55958d6263a61390ccd" Content-Length: 154375 Authorization: ESA base64エンコードしたサービスシークレットとライセンスキー Accept: */*" --a0b75a1f4b3cf55958d6263a61390ccd Content-Disposition: form-data; name="xml" <さっき作った$reqXmlはここに挿入> --a0b75a1f4b3cf55958d6263a61390ccd Content-Disposition: form-data; name="file"; filename="test_image.jpg" Content-Type: image/jpeg バイナリ --a0b75a1f4b3cf55958d6263a61390ccd--
まずは画像ファイルのオブジェクトである$cabinetUploadFileInfoをkeyを'file'という名前のarrayにして、httpPostという自作関数にぶち込みます。
function cabinetFileInsert($cabinetFileSetting, $cabinetUploadFileInfo) {
・・・省略
$cabinetUploadFileInfoArray = array(
'file' => $cabinetUploadFileInfo //アップロードするファイルのCabinetUploadFileInfoオブジェクトを渡す
);
$params = array(
'xml' => $reqXml
);
list($response,$httpStatusCode) = httpPost($url, $params, $cabinetUploadFileInfoArray);
...省略
}
$reqXmlもarrayにしてkeyは'xml'に指定てしてぶち込みます。
$cabinetUploadFileInfoの情報はxmlの下記部分(name="file"の部分)に使用され、
Content-Disposition: form-data; name="file"; filename="test_image.jpg" Content-Type: image/jpeg バイナリ
$reqXmlの情報はxmlの下記部分(name="xml"の部分)に使用されます。
Content-Disposition: form-data; name="xml" <さっき作った$reqXmlはここに挿入>
次は↑の情報からxmlを作ってfile_get_contentsで送る部分です。
/***
* 指定したURLに指定したパラメータのリクエストとファイルアップロードのPOSTを行う
*
* @param $url POSTするURL
* @param $params リクエストパラメーター
* @param $cabinetUploadFileInfoArray アップロードするファイル CabinetUploadFileInfoオブジェクト
*
* */
function httpPost($url, $params, $cabinetUploadFileInfoArray = []){
$isMultipart = (count($cabinetUploadFileInfoArray)) ? true : false;
$authkey = base64_encode(RMS_SERVICE_SECRET . ':' . RMS_LICENSE_KEY);
// RMSのファイル設定部分
$boundary = md5(mt_rand() . microtime());
$contentType = "Content-Type: multipart/form-data; boundary=\"$boundary\"";
$data = '';
foreach($params as $key => $value) {
$data .= "--$boundary" . "\r\n";
$data .= 'Content-Disposition: form-data; name=' . '"'. $key . '"' . "\r\n" . "\r\n";
$data .= $value . "\r\n";
}
// ファイルアップロード部分
foreach($cabinetUploadFileInfoArray as $key => $cabinetUploadFileInfo) {
$data .= "--$boundary" . "\r\n";
$data .= sprintf('Content-Disposition: form-data; name="%s"; filename="%s"%s', $key, basename($cabinetUploadFileInfo->filePath), "\r\n");
$data .= 'Content-Type: '. $cabinetUploadFileInfo->mimeType . "\r\n" . "\r\n";
$data .= file_get_contents($cabinetUploadFileInfo->filePath) . "\r\n";
}
$data .= "--$boundary--";
$headers = array(
"Connection: keep-alive",
"Proxy-Connection: keep-alive",
$contentType,
'Content-Length: '.strlen($data),
"Authorization: ESA {$authkey}",
"Accept: */*",
// "Accept-Encoding: gzip,deflate",
// "Accept-Language: ja,en-US;q=0.8,en;q=0.6"
);
$header = implode("\r\n", $headers);
customVarDump($header);
customVarDump($data);
$options = array('http' => array(
'method' => 'POST',
'ignore_errors' => true, //trueにすると40x,50x系のエラーでも内容を取得できる。
'content' => $data,
'header' => $header
));
$response = file_get_contents($url, false, stream_context_create($options));
$httpResponseHeader = implode("\r\n", $http_response_header);
$httpStatusCode = extract_response_http_code($httpResponseHeader);
return array($response, $httpStatusCode);
}
色々やってますが、要は最初に紹介した下記xmlを作成しています。
Connection: keep-alive
Proxy-Connection: keep-alive
Content-Type: multipart/form-data; boundary="a0b75a1f4b3cf55958d6263a61390ccd"
Content-Length: 154375
Authorization: ESA base64エンコードしたサービスシークレットとライセンスキー
Accept: */*"
--a0b75a1f4b3cf55958d6263a61390ccd
Content-Disposition: form-data; name="xml"
<?xml version="1.0" encoding="UTF-8"?>
<request>
<fileInsertRequest>
<file>
<fileName>test_siz_20180702125554</fileName>
<folderId>0</folderId>
<filePath>test_image.jpg</filePath>
<overWrite>true</overWrite>
</file>
</fileInsertRequest>
</request>
--a0b75a1f4b3cf55958d6263a61390ccd
Content-Disposition: form-data; name="file"; filename="test_image.jpg"
Content-Type: image/jpeg
バイナリ
--a0b75a1f4b3cf55958d6263a61390ccd--
ちなみにバイナリ部分はソースのどこで指定してんのってなると思うんですが、ここです。
$data .= "--$boundary" . "\r\n";
$data .= sprintf('Content-Disposition: form-data; name="%s"; filename="%s"%s', $key, basename($cabinetUploadFileInfo->filePath), "\r\n");
$data .= 'Content-Type: '. $cabinetUploadFileInfo->mimeType . "\r\n" . "\r\n";
$data .= file_get_contents($cabinetUploadFileInfo->filePath) . "\r\n"; // ★ここ
file_get_contentsにアップロードしたいファイルのパスを渡してやるとバイナリになって返却されます。それをxmlの文字列に .= で連結してるってわけ。何もテクいことはしておらず文字列連結のオンパレードでxml文字列を作っています。
$options = array('http' => array(
'method' => 'POST',
'ignore_errors' => true, //trueにすると40x,50x系のエラーでも内容を取得できる。
'content' => $data,
'header' => $header
));
$response = file_get_contents($url, false, stream_context_create($options));
度重なる文字列連結でhttpヘッダー($header)とそのbody($data)ができたら、あとは$optionsというarrayに諸々ぶち込みます。で、file_get_contentsに$optionsをいれて叩くと、作成したhttpヘッダ、body($optionsのcontentの部分)の内容でPOSTしてくれます。
レスポンスとして下記のようなのが返ってきたら成功です。
<?xml version="1.0" encoding="UTF-8"?>
<result>
<status>
<interfaceId>cabinet.file.insert</interfaceId>
<systemStatus>OK</systemStatus>
<message>OK</message>
<requestId>6a972b51-ae58-4823-9d08-1a546cb1983a</requestId>
<requests/>
</status>
<cabinetFileInsertResult>
<resultCode>0</resultCode>
<FileId>87866336</FileId>
</cabinetFileInsertResult>
</result>
FileIdの部分で、アップしたファイル固有のIDが返却されます。このIDを元にcabinet.files.search APIを叩くと、画像のURLや名前が取得できます。item.insert APIで商品を登録するときに画像のURLを指定する必要があるのですが、FileIdさえあればアップした画像を指定することが可能になります。
cabinet.files.search APIの解説はこちら(準備中)
item.insert APIの解説はこちら
まとめ
今回は画像登録API、cabinet.file.insertを解説しました。画像のアップロードは必須な上に手間がかかるめんどくさい作業です。本APIを使うことでこの作業を自動化し、商品の仕入れや受注配送の作業にもっと時間が避けるようになれば、売り上げを加速することが可能になるかもしれません。
今回作成したサンプルソースコードは下記です。
cabinet.file.insertのサンプルソースコード
その他のAPIについても下記から辿れるようになってますので、他のAPIも知りたいという方はチェックしてみてくださいね。