Google Drive APIで共有ドライブにファイルをアップロード

サーバーデータのバックアップを定期的にGoogleDriveに残していたので、Google Drive APIを導入して自動化してみました。その時の設定手順をメモしています。

前提

rootユーザーにてコマンドを実行します。

OSはCentOS8系で、PHPからGoogleAPIを呼び出します。

composerインストール

PHP アプリケーションのパッケージ・ライブラリの依存関係を管理するツール「Composer」をインストールします。

# cd /root
# curl -sS https://getcomposer.org/installer | php

google api インストール

先ほどインストールしたComposerを使用して、google apiをインストールします。

# php composer.phar require google/apiclient:^2.0

GoogleDriveAPIの有効化

Google Cloud PlatformのウィザードからGoogleDriveAPIを有効にします。 https://console.cloud.google.com/flows/enableapi?apiid=drive

サーバの設定

client_secret.jsonを落としてきて/root直下に配置

/root配下にupload_large.phpを作成します。

upload.php
<?php
ini_set("memory_limit", "-1");

require __DIR__ . '/vendor/autoload.php';

if (php_sapi_name() != 'cli') {
    throw new Exception('This application must be run on the command line.');
}

/**
 * Returns an authorized API client.
 * @return Google_Client the authorized client object
 */
function getClient()
{
    $client = new Google_Client();
    $client->setApplicationName('Google Drive API Upload');
    $client->setScopes(Google_Service_Drive::DRIVE);
    $client->setAuthConfig('/root/credentials.json');
    $client->setAccessType('offline');
    $client->setPrompt('select_account consent');

    // Load previously authorized token from a file, if it exists.
    // The file token.json stores the user's access and refresh tokens, and is
    // created automatically when the authorization flow completes for the first
    // time.
    $tokenPath = '/root/client_secret.json';
    if (file_exists($tokenPath)) {
        $accessToken = json_decode(file_get_contents($tokenPath), true);
        $client->setAccessToken($accessToken);
    }

    // If there is no previous token or it's expired.
    if ($client->isAccessTokenExpired()) {
        // Refresh the token if possible, else fetch a new one.
        if ($client->getRefreshToken()) {
            $client->fetchAccessTokenWithRefreshToken($client->getRefreshToken());
        } else {
            // Request authorization from the user.
            $authUrl = $client->createAuthUrl();
            printf("Open the following link in your browser:\n%s\n", $authUrl);
            print 'Enter verification code: ';
            $authCode = trim(fgets(STDIN));

            // Exchange authorization code for an access token.
            $accessToken = $client->fetchAccessTokenWithAuthCode($authCode);
            $client->setAccessToken($accessToken);

            // Check to see if there was an error.
            if (array_key_exists('error', $accessToken)) {
                throw new Exception(join(', ', $accessToken));
            }
        }
        // Save the token to a file.
        if (!file_exists(dirname($tokenPath))) {
            mkdir(dirname($tokenPath), 0700, true);
        }
        file_put_contents($tokenPath, json_encode($client->getAccessToken()));
    }
    return $client;
}

$path = $argv[1];

if (file_exists($path)) {
  // Get the API client and construct the service object.
  $client = getClient();

  $service = new Google_Service_Drive($client);

  // ※driveIdとparentsの「XXXXXXXXXXXXXXX」には、 各自の設定を入れるようになります。
  $file = new Google_Service_Drive_DriveFile();
  $file->name = basename($path);
  $file->driveId = 'XXXXXXXXXXXXXXX';
  $file->parents = array("XXXXXXXXXXXXXXX"); 
  $chunkSizeBytes = 100 * 1024 * 1024;


  // Call the API with the media upload, defer so it doesn't immediately return.
  $client->setDefer(true);
  $request = $service->files->create($file, array(
      'supportsAllDrives'=> true,
      'fields' => 'id'
  ));

  // Create a media file upload to represent our upload process.
  $media = new Google\Http\MediaFileUpload(
      $client,
      $request,
      'text/plain',
      null,
      true,
      $chunkSizeBytes
  );
  $media->setFileSize(filesize($path));


  // Upload the various chunks. $status will be false until the process is
  // complete.
  $status = false;
  $handle = fopen($path, "rb");
  while (!$status && !feof($handle)) {
    // read until you get $chunkSizeBytes from TESTFILE
    // fread will never return more than 8192 bytes if the stream is read buffered and it does not represent a plain file
    // An example of a read buffered file is when reading from a URL
    $chunk = readVideoChunk($handle, $chunkSizeBytes);
    $status = $media->nextChunk($chunk);
  }

  // The final value of $status will be the data from the API for the object
  // that has been uploaded.
  $result = false;
  if ($status != false) {
    $result = $status;
  }

  fclose($handle);
}

function readVideoChunk ($handle, $chunkSize)
{
    $byteCount = 0;
    $giantChunk = "";
    while (!feof($handle)) {
        // fread will never return more than 8192 bytes if the stream is read buffered and it does not represent a plain file
        $chunk = fread($handle, 8192);
        $byteCount += strlen($chunk);
        $giantChunk .= $chunk;
        if ($byteCount >= $chunkSize)
        {
            return $giantChunk;
        }
    }
    return $giantChunk;
}

第1引数に指定されたファイル(下の例だと/root/test.ext)をアップロードします。

# php /root/upload_large.php /root/test.txt

あとはcronの設定でバックアップを指定することで無事、自動化が完了しました。