Google App Engine for PHPでGoogle Drive APIを使う

Google App Engine for PHP(GAE/PHP)では残念ながらBlobstoreの利用がサポートされていません。そこでGoogleが提供しているGoogle DriveとAPIライブラリを用いてGAE/PHPからGoogle Driveへアクセスしてみます。
(2016.07.07 Google Cloud Platformが刷新されたため情報が古くなっています)

## 1. API利用設定
[Developers Console](https://console.developers.google.com/)へアクセスし、APIの設定からDrive APIを有効にします。
![api.jpg](https://qiita-image-store.s3.amazonaws.com/0/70925/8a42fb76-72d1-18a2-df99-d01a9b773f4e.jpeg)


![driveapi.jpg](https://qiita-image-store.s3.amazonaws.com/0/70925/b26d118e-f5fd-4a62-d6a3-fbe75da36888.jpeg)

<A NAME=OAuth />
## 2. OAuthの設定
Developers Consoleの[認証情報]からOAuth認証によるクライアントIDの取得を行います。[新しいクライアントIDを作成]ボタンを押すと設定画面が表示されます。アプリケーションの種類は[ウェブアプリケーション]を選択し、[同意画面を設定]ボタンを押します。
![client.jpg](https://qiita-image-store.s3.amazonaws.com/0/70925/9b42b4de-5f62-ac00-b02c-be386d256ba6.jpeg)

同意画面でサービス名に任意の値を記入して[保存]ボタンを押します。
![mail.jpg](https://qiita-image-store.s3.amazonaws.com/0/70925/6a482614-ea87-10ac-81a0-d0e82b8f1ed9.jpeg)


クライアントID作成画面に戻ります。承認済みのリダイレクトURIを入力するフォームにはOAuth認証後に戻ってくるURLを入力します。
![client2.jpg](https://qiita-image-store.s3.amazonaws.com/0/70925/1afdb74a-cf4b-ed6f-e274-155178542df7.jpeg)

[クライアントIDを作成]ボタンを押すとクライアントIDが作成されます。
![secret.jpg](https://qiita-image-store.s3.amazonaws.com/0/70925/0657663e-b0aa-6e7a-dd29-9ce7b142d4a4.jpeg)
これから作成するプロジェクトはこのクライアントID、クライアントシークレット、リダイレクトURIを使用します。

## 3. Google APIs Client Library for PHPのダウンロード
PHPからGoogleのAPIへアクセスするためのライブラリをダウンロードします。ライブラリはGoogleが提供している下記のリンクからダウンロードすることができます。APIに関する情報やリファレンスは[Developers Documentation](https://developers.google.com/api-client-library/php/start/get_started)が参考になります。

[google/google-api-php-client · GitHub](https://github.com/google/google-api-php-client)

ダウンロードしたライブラリをプロジェクトファイルと同じフォルダに保存します。

## 4. プログラム
### 4.1 OAuth認証
Google Drive APIへのアクセスはOAuth認証を必要とします。<A HREF=#OAuth>2. OAuthの設定</A>で取得したクライアントID、クライアントシークレット、リダイレクトURIを下記のサンプルに記述します。

``` php:sample.php
<?php
// ライブラリ読み込み
require_once('./google-api-php-client/src/Google/autoload.php');

// セッションスタート
session_start();
$client = new Google_Client();
// クライアントID
$client->setClientId('クライアントID');
// クライアントシークレット
$client->setClientSecret('クライアントシークレット');
// リダイレクトURI
$client->setRedirectUri('リダイレクトURI');

// 許可されてリダイレクトされると URL に code が付加されている
// code があったら受け取って、認証する
if (isset($_GET['code'])) {
    // 認証
    $client->authenticate($_GET['code']);
    $_SESSION['token'] = $client->getAccessToken();
    // リダイレクト GETパラメータを見えなくするため（しなくてもOK）
    header('Location: http://'.$_SERVER['HTTP_HOST'].$_SERVER['PHP_SELF']);
    exit;
}

// セッションからアクセストークンを取得
if (isset($_SESSION['token'])) {
    // トークンセット
    $client->setAccessToken($_SESSION['token']);
}

// トークンがセットされていたら
if ($client->getAccessToken()) {
    try {
        echo "Google Drive Api 連携完了！<br>";
        $_SESSION['client'] = $client;
    } catch (Google_Exception $e) {
        echo $e->getMessage();
    }
} else {
    // 認証用URL取得
    $client->setScopes(Google_Service_Drive::DRIVE);
    $authUrl = $client->createAuthUrl();
    echo '<a href="'.$authUrl.'">アプリケーションのアクセスを許可してください。</a>';
    exit;
}

?>
<a href="list.php">ファイル一覧</a><br>
<a href="imageview.php">画像表示</a><br>
<a href="upload.php">アップロード</a>
```

<A NAME=list />
### 4.2 ファイル一覧
Google Driveに保存されているファイルの一覧を取得します。

``` php:list.php
<?php
require_once('./google-api-php-client/src/Google/autoload.php');
require_once('./google-api-php-client/src/Google/Service/Drive.php');
//$client変数の型を明示させるためsession_start()はライブラリ読み込みの後に記述
session_start();

$client = $_SESSION['client'];
$service = new Google_Service_Drive($client);

try {
    // 親フォルダ
    // root でマイドライブ, root 以外は名前ではなく ID を指定
    $parents = 'root';
    if (isset($_GET['parents'])) {
        $parents = htmlspecialchars($_GET['parents'], ENT_QUOTES);
    }
    // 次ページに移動する場合に渡すトークン
    $pageToken = null;
    if (isset($_GET['pageToken'])) {
        $pageToken = $_GET['pageToken'];
    }
    $parameters = array('q' => "'{$parents}' in parents", 'maxResults' => 20);
    if ($pageToken) {
        $parameters['pageToken'] =$pageToken;
    }
    // ファイルリスト取得, Google_Service_Drive_FileList のオブジェクトが返ってくる
    $files = $service->files->listFiles($parameters);
    // ファイルの一覧データ
    $results = $files->getItems();
    // 次ページのトークン取得, ない場合は NULL
    $pageToken = $files->getNextPageToken();
    // 結果表示
    foreach ($results as $result) {
        if ($result->mimeType == 'application/vnd.google-apps.folder') {
            echo 'フォルダ ：<a href="http://'.$_SERVER['HTTP_HOST'].$_SERVER['PHP_SELF'].'?parents='.urlencode($result->id).'">'.$result->title.'</a> ID:'.$result->id.'<br />';
        } else {
            echo 'ファイル ：<a href="read.php?id='.$result->id.'"">'.$result->title.'</a> ID：'.$result->id.'<br />';
        }
    }
    // pageToken があったら次ページヘのリンク表示
    if ($pageToken) {
        echo '<a href="http://'.$_SERVER['HTTP_HOST'].$_SERVER['PHP_SELF'].'?parents='.urlencode($parents).'&pageToken='.urlencode($pageToken).'">次ページ</a>';
    }
}catch(Google_Exception $e){
    echo $e->getMessage();
}
```

### 4.3 画像表示
Google Driveに保存してある画像を表示するプログラムです。

``` php:imageview.php
<?php
require_once('./google-api-php-client/src/Google/autoload.php');
require_once('./google-api-php-client/src/Google/Service/Drive.php');

session_start();

$client = $_SESSION['client'];
$service = new Google_Service_Drive($client);

try {
    // 親フォルダ
    // root でマイドライブ, root 以外は名前ではなく ID を指定
    $parents = 'root';
    if (isset($_GET['parents'])) {
        $parents = htmlspecialchars($_GET['parents'], ENT_QUOTES);
    }
    // 次ページに移動する場合に渡すトークン
    $pageToken = null;
    if (isset($_GET['pageToken'])) {
        $pageToken = $_GET['pageToken'];
    }
    $parameters = array('q' => "'{$parents}' in parents", 'maxResults' => 20);
    if ($pageToken) {
        $parameters['pageToken'] =$pageToken;
    }
    // ファイルリスト取得, Google_Service_Drive_FileList のオブジェクトが返ってくる
    $files = $service->files->listFiles($parameters);
    // ファイルの一覧データ
    $results = $files->getItems();
    // 次ページのトークン取得, ない場合は NULL
    $pageToken = $files->getNextPageToken();
    // 結果表示
    foreach ($results as $result) {
        // 拡張子がjpgであれば表示
        if ($result->fileExtension == 'jpg') {
            echo "<img border=0 src={$result->webContentLink}><img border=0 src={$result->thumbnailLink}><br>";
        }
    }
    // pageToken があったら次ページヘのリンク表示
    if ($pageToken) {
        echo '<a href="http://'.$_SERVER['HTTP_HOST'].$_SERVER['PHP_SELF'].'?parents='.urlencode($parents).'&pageToken='.urlencode($pageToken).'">次ページ</a>';
    }
}catch(Google_Exception $e){
    echo $e->getMessage();
}
```

マイドライブの直下であれば親フォルダはroot、フォルダを指定する場合はフォルダIDを設定します。フォルダIDは<A HREF=#list>4.2 ファイル一覧</A>か、ブラウザでGoogle Driveへアクセスし対象のフォルダを選択するとURLからIDを確認することができます。

`https://drive.google.com/drive/#folders/{フォルダID}`

![url.jpg](https://qiita-image-store.s3.amazonaws.com/0/70925/44b59029-a021-4f50-fabd-a48753fdf59d.jpeg)

またファイルは[Files](https://developers.google.com/drive/v2/reference/files)で確認できるように拡張子、サムネイルリンク、web参照リンクなどを返すメソッドが用意されています。サンプルではファイル拡張子のjpgを判断して表示させています。

### 4.4 アップロード
ファイルのアップロードはinsertメソッドを使用します。注意点としてinsertメソッドの第2引数のarray()には 'uploadType' => 'multipart'を記述しないと空のファイルが作成されてしまいます。

``` php:upload.php
<?php
require_once('./google-api-php-client/src/Google/autoload.php');
require_once('./google-api-php-client/src/Google/Service/Drive.php');
//session_start()はライブラリ読み込みの後に記述
session_start();

$client = $_SESSION['client'];
$service = new Google_Service_Drive($client);

// 追加したいファイルオブジェクトを作成
$file = new Google_Service_Drive_DriveFile();
$file->setTitle('new_test.txt');
$file->setDescription('PhpTest');
$file->setMimeType('text/plain');

$data = 'aaaaaaaaaaaaaaabbbbbbbbbbbbbbbbccccccccccccc';
//$data = file_get_contents("./new_test.txt");

// 親フォルダの ID
$parentId = 'root';
// 親オブジェクト
$parent = new Google_Service_Drive_ParentReference();
$parent->setId($parentId);
$file->setParents(array($parent));

try {
    $result = $service->files->insert($file, array(
        'data' => $data,
        'mimeType' => 'text/plain',
        'uploadType' => 'multipart'
    ));

    if($result) {
        echo "アップロード完了";
    } else {
        echo "アップロード失敗";
    }
} catch (Exception $e) {
    print "An error occurred: " . $e->getMessage();
}
```

### 4.5 ファイルの読み込み
テキストファイルの内容を読み込みます。

``` php:read.php
<?php
require_once('./google-api-php-client/src/Google/autoload.php');
require_once('./google-api-php-client/src/Google/Service/Drive.php');
session_start();

$client = $_SESSION['client'];
$service = new Google_Service_Drive($client);
$id = $_GET['id'];

echo "ファイルID：{$id}<br>";

try {
    $file = $service->files->get($id);
    $downloadUrl = $file->getDownloadUrl();
    $request = new Google_Http_Request($downloadUrl, 'GET', null, null);
    $httpRequest = $service->getClient()->getAuth()->authenticatedRequest($request);
    if ($httpRequest->getResponseHttpCode() == 200) {
        echo "ファイル内容<br>";
        echo $httpRequest->getResponseBody();
    } else {
        // An error occurred.
        return null;
    }
} catch(Google_Exception $e) {
    echo $e->getMessage();
}
```
参考
[PHPでGoogle Drive Apiを使用する | ScrapEngineer](http://scrap.php.xdomain.jp/google_drive_api/)
[[PHP] Google Drive API を使って Google Drive のファイル一覧取得、ファイルの追加を行う | unlinked log](http://log.noiretaya.com/193)
[Google Developers](https://developers.google.com/drive/web/quickstart/quickstart-php)