kintoneへのデータ移行 ~ SDKで文字とファイルを登録 ~

システムのデータを移行する際には文字データだけでなく、ファイルデータも移行することが多いと思います。
kintoneではAPIを使うことで文字データだけでなく、ファイルデータも登録できますし、APIの仕様もdeveloper networkに掲載されています。
ただ、具体的な移行スクリプトの例がないので作ってみました。

- developer networkの説明
 - [データの登録](https://cybozudev.zendesk.com/hc/ja/articles/202166160)
 - [ファイルアップロード](https://cybozudev.zendesk.com/hc/ja/articles/201941824)
 - [ファイルアップロードで必須となる3つの手順](https://cybozudev.zendesk.com/hc/ja/articles/200724665)

# 前提
今回はPHPとkintone API用のSDK（[cybozu-http](https://github.com/ochi51/cybozu-http)）を利用します。

- プログラムの実行環境（今回はRedhatを利用）
- PHP5.5以降がインストール済
- cybozu-httpがインストール済
- kintoneのフォーム設定は以下の通り


| フィールド名 | フィールドタイプ | フィールドコード |
|----------|----------------|------------------|
| 登録日 | 日付 | date |
| 担当者 | ユーザー選択 | pic |
| タイトル | 文字列（1行） | title |
| 添付ファイル | 添付ファイル | attacheFile |

- フォルダ構成は以下の通り

```bash
$ find ./migration.php ./migration -type f
./migration.php
./migration/2016-03-01/01/ex/IMG_0950.JPG
./migration/2016-03-01/01/プレゼンテーション.pptx
./migration/2016-03-01/01/info.csv
./migration/2016-03-02/01/IMG_0555.JPG
./migration/2016-03-02/01/info.csv
./migration/2016-03-02/02/ex/出張報告.xlsx
./migration/2016-03-02/02/info.csv

$ cat ./migration/2016-03-01/01/info.csv
"kintoneプレゼン","momota|tamai","ex/IMG_0950.JPG|./プレゼンテーション.pptx"

$ cat ./migration/2016-03-02/01/info.csv
"タイトルだよ""も大丈夫だよ","momota","./IMG_0555.JPG"

$ cat ./migration/2016-03-02/02/info.csv
"ガルーン新規構築","tamai","ex/出張報告.xlsx"
```

- フォルダ構成の説明
 - migration.phpが実行ファイル
 - migrationフォルダ配下の年月日をフィールド「登録日」に登録
 - info.csvにその他フィールドに登録する情報を記載
 - info.csvの1列目はフィールド「タイトル」に登録
 - info.csvの2列目はフィールド「担当者」に登録（複数の場合は「|」区切り）
 - info.csvの3列目は添付ファイルへの相対パスで、フィールド「添付ファイル」にファイルの実体を登録（複数の場合は「|」区切り）

- migration.phpのソースは以下の通り

```php:migration.php
#!/usr/bin/env php
<?php
require 'vendor/autoload.php';

// 移行対象ディレクトリ
$migrationDir = './migration';

// cybozu.comのサブドメイン
$subDomain = 'xxxxxxxx';

// アプリID
$appId = 'oo';

// APIトークン
$apiToken = 'XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX';


$api = new \CybozuHttp\Api\KintoneApi(\CybozuHttp\Client::factory([
    'domain' => 'cybozu.com',
    'subdomain' => $subDomain,
    'use_api_token' => true,
    'token' => $apiToken
]));


$infoFileList = searchInfoFile($migrationDir);
foreach ($infoFileList as $infoFile) {
    $pathList = explode(DIRECTORY_SEPARATOR, $infoFile);
    $date = array_slice($pathList, -3)[0];

    $csvFile = new SplFileObject($infoFile);
    $csvFile->setFlags(SplFileObject::READ_CSV);
    foreach ($csvFile as $row) {
        // 空行はスキップ
        if(is_null($row[0])) {
            continue;
        }

        $title = $row[0];
        $picList = $row[1];
        $attacheList = $row[2];

        $postRecord = [
            'date' => [
                'value' => $date
            ],
            'title' => [
                'value' => $title
            ],
            'pic' => [
                'value' => []
            ],
            'attacheFile' => [
                'value' => []
            ]
        ];

        foreach (explode('|', $picList) as $pic) {
            $postRecord['pic']['value'][] = ['code' => $pic];
        }

        foreach (explode('|', $attacheList) as $attache) {
            $attacheFilePath = dirname($infoFile) . DIRECTORY_SEPARATOR . $attache;
            $fileKey = $api->file()->post($attacheFilePath);
            $postRecord['attacheFile']['value'][] = ['fileKey' => $fileKey];
        }

        $postRes = $api->record()->post($appId, $postRecord);
        if (array_key_exists('revision', $postRes) !== true) {
            echo 'ERROR' . $res;
        }
    }
}


// 移行対象ディレクトリ配下のinfo.csvを探索
function searchInfoFile($dir) {
    $iterator = new RecursiveDirectoryIterator($dir);
    $iterator = new RecursiveIteratorIterator($iterator);

    $list = [];
    foreach ($iterator as $fileinfo) {
        if (($fileinfo->isFile()) && ($fileinfo->getfilename() === 'info.csv')) {
            $list[] = $fileinfo->getPathname();
        }
    }

    return $list;
}
?>
```

# 結果
migration.phpを実行すると・・・

```bash
$ ./migration.php
$ 
```

上手くいきました！文字だけでなくファイルも登録できました！
![result.jpg](https://qiita-image-store.s3.amazonaws.com/0/102790/3568cecc-df8d-c03c-900b-1a80c22ba8b2.jpeg)

100行に満たないプログラムで特定ディレクトリ配下にあるデータを移行することができました。
簡単そうですよね。（エラー処理は入っていませんが・・・）

# 参考
ネットワークの転送速度にかなり依存しますが、参考までに登録にかかる時間を計ってみました。

- 100レコード、100ファイル（計150Mbyte）の登録にかかった時間
 - 1回目　　：24秒
 - 2回目　　：23秒
 - 3回目　　：25秒
 - 3回の平均：24秒

- 100レコード、100ファイル（計7300Mbyte）の登録にかかった時間
 - 1回目　　：434秒
 - 2回目　　：434秒
 - 3回目　　：426秒
 - 3回の平均：432秒

※APIを連続で実行する場合、可能であれば[アプリグループをPrivate](https://help.cybozu.com/ja/k/admin/whats_appgroup.html)にして実行しましょう。
　PrivateアプリはAPIの実行回数にカウントされません。

# kintone開発者ライセンス
移行の検証にも使える無料の[開発者ライセンスはこちら](https://cybozudev.zendesk.com/hc/ja/)