Cronを使って自作バックアップファイルをDropboxに自動転送する方法

「ホームページのデータを自動でバックアップさせて、さらにそれをDropboxに自動転送させたいんだけど、どうやったらできるの?」

自動バックアップ⇒Dropboxに自動転送を実装するには、プログラムを組んでそのプログラムをCronを使って指定した時間に動かせば良いだけです。
これらをシステム化する流れは以下の通りです。

  1. Dropbox APIの利用者登録をしてアクセストークンを取得する。
  2. WEBサーバー上に所定のファイルとフォルダーを作成して、一定の情報を変数に代入する。
  3. 自動バックアップ⇒Dropboxに自動転送するプログラムを走らせる時間のCron設定をする。

プログラムを組むにあたり、私が利用した言語はphpです。
他の言語でも実装できるようですが、私がわかるのはphpだけなのでご容赦ください。
また、ほとんど自分用に作ったところもあるので、私のWEB環境と異なる場合は予期しないエラーが発生するかもしれません。

私のWEB環境
サーバーXserver
PHP Ver.7.2

Dropboxのアカウントをお持ちでない方は、はじめにDropboxアカウントを取得してください。
アカウント取得後は、保存したいファイルサイズに合わせてプランを選択してください。

さて、準備はよろしいでしょうか。
それでは順を追ってご説明いたします。

Dropbox APIの利用者登録をしてアクセストークンを取得する

まずはHTTP開発者向けのDropboxページを開いてください。
続いて、ページ右上の「Add console」から「Create app」に進んでください。

今開いているページは「Create a new app on the DBX Platform」と表示されていると思います。
選択項目は以下のように選んでください。

1. Choose an API
Dropbox Business APIの意味と利用目的が分からない場合は、Dropbox APIを選択してください。
2. Choose the type of access you need
Full Dropboxにアクセスする必要は全くないので、リスクの少ないApp folderを選択してください。
3. Name your app
好きなアプリ名をつけてください。これはバックアップファイルが保存されるフォルダ名にもなります。すでに登録されているアプリ名はエラーになるのでご注意ください。

この記事を執筆しているのは2020年3月10日なので、時期によってはレイアウトが少し変わっているかもしれません。

選択と入力が終わったら「Create app」をクリックしてください。
問題が無ければ今作成したアプリの設定ページが開きます。

アプリの設定ページに「Generated access token」という項目があるので、「Generate」をクリックしてアクセストークンを表示してください。
表示されたアクセストークンをコピーして、一旦メモ帳にペーストするなどしておくと良いでしょう。

WEBサーバー上に所定のファイルとフォルダーを作成して、一定の情報を変数に代入する

まずはルートディレクトリに「backup」という名前のフォルダーを作ってください。
次に今作成した「backup」フォルダーの中に、以下のファイルとフォルダーを作ってください。

  • backup
    • backuped_data
    • backup_database.php
    • backup_webdata.php
    • info.php
    • TransferBackUPFiles.php

ルートディレクトリがよくわからないかたは、以下のツリー構造の画像をご覧ください。

上の画像はPikoPikoBlogのツリー構造です。
サーバーはXserverなので、「public_html」がルートディレクトリになっています。

他のサーバーだと、「public_html」がルートディレクトリになるとは限りません。
例えばさくらのレンタルサーバの場合、ドメイン利用設定のWeb公開フォルダに設定した任意のフォルダ名がルートディレクトリになります。

続いて、「backup」ファルダ―内に作成したファイルに、以下に記述しているプログラムコードを書き込んでください。

backup_database.phpに書き込むコード

<?php

require_once(dirname(__FILE__) . '/info.php');


/*----------保存設定----------*/
$dirpath = dirname(__FILE__) . '/backuped_data/';
$filename = "bkupdb_" . date('YmdHis') . ".sql";
$savePath = $dirpath . $filename;


/*----------バックアップファイルを保存----------*/
$command = 'mysqldump --single-transaction --default-character-set=binary ' . $DBNAME . ' --host=' . $HOST . ' --user=' . $USER . ' --password=' . $PASSWORD . ' > ' . $savePath;
system($command);
chmod($savePath, 0700);// セキュリティのためにバックアップファイルのパーミッション変更


/*----------古いバックアップファイルを削除----------*/
$period_days = 3;// 3日以上前のファイルを削除
$delete_time = time() - 60 * 60 * 24 * $period_days;
$d_handle = opendir($dirpath);
while($d_targetFile = readdir($d_handle)){
  if (preg_match("/bkupdb/", $d_targetFile)) {
    $d_path = $dirpath . $d_targetFile;
    $filetime = filemtime($d_path);
    if($filetime < $delete_time){
      unlink("{$d_path}");
    }
  }
}
closedir($d_handle);


/*----------バックアップファイルをDropboxにアップロードする記述をしたphpファイル を実行----------*/
require_once(dirname(__FILE__) . '/TransferBackUPFiles.php');

?>

backup_database.phpは、データベースのデータをWEBサーバー上にバックアップします。
私はbackup_database.phpに書き込んだプログラムをCronで毎日走らせているので、3日以上前のバックアップファイルは、WEBサーバー上から削除するようにしています。

backup_webdata.phpに書き込むコード

<?php

require_once(dirname(__FILE__) . '/info.php');


/*----------保存設定----------*/
$dirpath = dirname(__FILE__) . '/backuped_data/';
$filename = "bkupweb_" . date('YmdHis') . ".tar.gz";
$savePath = $dirpath . $filename;


/*----------バックアップファイルを保存----------*/
$saveTarget = dirname(__FILE__) . '/../' . $saveTargetFolder . '/';
system("tar -zcvf " . $savePath . " -C / " . $saveTarget);
chmod($savePath, 0700);// セキュリティのためにバックアップファイルのパーミッション変更


/*----------古いバックアップファイルを削除----------*/
$period_days = 7;// 7日以上前のファイルを削除
$delete_time = time() - 60 * 60 * 24 * $period_days;
$d_handle = opendir($dirpath);
while($d_targetFile = readdir($d_handle)){
  if (preg_match("/bkupweb/", $d_targetFile)) {
    $d_path = $dirpath . $d_targetFile;
    $filetime = filemtime($d_path);
    if($filetime < $delete_time){
      unlink("{$d_path}");
    }
  }
}
closedir($d_handle);


/*----------バックアップファイルをDropboxにアップロードする記述をしたphpファイル を実行----------*/
require_once(dirname(__FILE__) . '/TransferBackUPFiles.php');

?>

backup_webdata.phpは、ホームページやブログを構成している画像やHTMLファイルなどのWEBデータをWEBサーバー上にバックアップします。
私はbackup_webdata.phpに書き込んだプログラムをCronで毎週日曜日に走らせているので、7日以上前のバックアップファイルは、WEBサーバー上から削除するようにしています。

info.phpに書き込むコード

<?php

// 基本情報
// データベース関係
$DBNAME = '●●●●';// データベース名
$HOST = '●●●●';// ホスト名またはサーバ名
$USER = '●●●●';// データベースログインアカウントのユーザー名
$PASSWORD = '●●●●';// データベースログインアカウントのパスワード

// WEBデータ関係
$saveTargetFolder = '●●●●';// 保存したいWEBデータの最上位フォルダー

// Dropbox関係
$dropbox_dir_last = "●●●●";// バックアップファイルの転送先となるDropboxのフォルダー名
$token = '●●●●';// Dropbox APIのアクセストークン

?>

上記コードを書き込んだら、「●●●●」になっている部分に自動バックアップ⇒Dropboxに自動転送をするために必要な全ての基本情報を書き込みます。
データベース関係の基本情報の調べ方については、ググればいくらでも情報が見つかるので、ここでの説明は控えます。

WEBデータ関係の基本情報に「保存したいWEBデータの最上位フォルダー」とありますが、ここがルートディレクトリになる場合は、WEBデータのバックアップファイルサイズが大きくなります。
というのも、WEBデータのバックアップファイルの保存先がルートディレクトリ上にあるので、ルートディレクトリごと保存してしまうとバックアップファイルのバックアップもすることになってしまうからです。

PikoPikoBlogの場合は、ルートディレクトリ上に作った「wp」フォルダーにブログを構成しているWEBデータを展開しているので、保存したいWEBデータの最上位フォルダーを「wp」にしています。
これならバックアップファイルの保存先である「backup」ファルダーは含まれないので、ファイルサイズを抑えることが出来ます。

Dropbox関係の基本情報に必要なのは「バックアップファイルの転送先となるDropboxのフォルダー名」と「Dropbox APIのアクセストークン」です。
「バックアップファイルの転送先となるDropboxのフォルダー名」は任意で決めてください。
私は「pikopikoblog」にしました。

「Dropbox APIのアクセストークン」には前項で取得したものを書き込んでください。

TransferBackUPFiles.phpに書き込むコード

<?php

/*----------呼び出し元のファイル情報を取得----------*/
$associative_array = debug_backtrace();
if (preg_match("/backup_database/", $associative_array[0]["file"])) {
  $preg_match_str = "bkupdb";
  $delete_cnt = 10;// Dropboxに残したいファイル数
}elseif (preg_match("/backup_webdata/", $associative_array[0]["file"])) {
  $preg_match_str = "bkupweb";
  $delete_cnt = 3;// Dropboxに残したいファイル数
}else{
  exit();
}


/*----------ファイルを削除する準備としてファイル情報を検索して変数に格納する----------*/
$url = "https://api.dropboxapi.com/2/files/search_v2";

$headers = [
  'Authorization: Bearer '.$token,
  'Content-Type: application/json'
];

$post_fields = [
  "query" => $preg_match_str,"include_highlights" => false // 検索したいファイルのパス
];

$options = [
  CURLOPT_URL => $url,
  CURLOPT_RETURNTRANSFER => true,
  CURLOPT_HTTPHEADER => $headers,
  CURLOPT_POST => true,
  CURLOPT_POSTFIELDS => json_encode($post_fields)
];

$ch = curl_init();
curl_setopt_array($ch, $options);
$result = curl_exec($ch);
curl_close($ch);
// $resultの中身を見たい時 var_dump($result);

// ファイル名を配列に格納する
// $result内のファイル名は新しい順で並んでいるものとする
// もし並び順に規則性が無いなら考え直す必要あり
$str = explode(",",$result); //配列に格納する
$cnt = count($str); //配列数を調べる
$baseval = 15;
$max = ($cnt - 1) / $baseval; //データの1塊りに含まれるカンマ区切りの値は15なのでこのような式になる
// $maxの中身を見たい時 echo "max=" . $max . "<br><br>";

for($i = 0; $i < $max; $i++){
  $num = 2 + $baseval * $i;
  $delete_filename[$i] = $str[$num];

  $table = array(
    '"' => '',
    'name:' => ''
  );
  $search = array_keys( $table);
  $replace = array_values( $table);

  $delete_filename[$i] = trim(str_replace($search,$replace,$str[$num]));
  // $delete_filename[$i]の中身を見たい時 echo $num . " " . $i . " " . $delete_filename[$i] . "<br>\n";
}


/*----------ファイルを削除する----------*/
$url = "https://api.dropboxapi.com/2/files/delete_v2";

$headers = [
  'Authorization: Bearer '.$token,
  'Content-Type: application/json'
];

for($i = $delete_cnt - 1; $i < $max; $i++){
  $post_fields = [
    "path" => "/" . $dropbox_dir_last . "/" . $delete_filename[$i] // 削除したいファイルのパス
  ];

  $options = [
    CURLOPT_URL => $url,
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_HTTPHEADER => $headers,
    CURLOPT_POST => true,
    CURLOPT_POSTFIELDS => json_encode($post_fields)
  ];

  $ch = curl_init();
  curl_setopt_array($ch, $options);
  $result = curl_exec($ch);
  curl_close($ch);
  // var_dump($result)の中身を見たい時 var_dump($result);
}


/*----------Dropboxに最新のバックアップファイルを転送する----------*/
$latest_mtime_bkupfile = 0;
if ($handle = opendir($dirpath)) {
  while (false !== ($file = readdir($handle))) {
    if ($file != "." && $file != "..") {
      $fname = $dirpath . $file;

      if (preg_match("/{$preg_match_str}/", $fname)) {
        $mtime_bkupfile = filemtime( $fname );
        if($mtime_bkupfile > $latest_mtime_bkupfile){
          $latest_mtime_bkupfile = $mtime_bkupfile;
          $latest_fname_bkupfile = $fname;
        }
      }
    }
  }
  closedir($handle);
}

$path = $latest_fname_bkupfile;
$filename = str_replace($dirpath, '', $latest_fname_bkupfile);
$fp = fopen($path, 'rb');
$size = filesize($path);

$cheaders = array('Authorization: Bearer '.$token,
                  'Content-Type: application/octet-stream',
                  'Dropbox-API-Arg: {"path":"/'.$dropbox_dir_last."/".$filename.'", "mode":"add"}'); //Dropbox側の保存先のフォルダ指定。存在しない場合は勝手に作ってくれる。

$ch = curl_init('https://content.dropboxapi.com/2/files/upload');
curl_setopt($ch, CURLOPT_HTTPHEADER, $cheaders);
curl_setopt($ch, CURLOPT_PUT, true);
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'POST');
curl_setopt($ch, CURLOPT_INFILE, $fp);
curl_setopt($ch, CURLOPT_INFILESIZE, $size);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
$response = curl_exec($ch);
// $responseの中身を見たい時 echo $response;
curl_close($ch);
fclose($fp);


?>

TransferBackUPFiles.phpは、Dropboxに保存されている古いバックアップファイルを削除して、WEBサーバー上にある新しいバックアップファイルを転送します。
PikoPikoBlogの場合、データベースのバックアップファイルは重くないのですぐに転送されるのですが、WEBデータのバックアップファイルはかなりの重さなので、転送されるまでに時間がかかります。

具体的にはデータベースのバックアップファイルサイズが524 KBで、転送時間は1秒か2秒くらいです。
WEBデータのバックアップファイルサイズは201,336 KBで、転送時間は90秒くらいです。
ファイルサイズが大きすぎると、保存できなくなるリスクもあるので注意してください。

自動バックアップ⇒Dropboxに自動転送するプログラムを走らせる時間のCron設定をする

指定した時間にプログラムを走らせるようにCronの設定をしましょう。
エックスサーバーではサーバーパネルからCron設定をすることが出来ます。
Cron設定に追加するコマンドは2つで、PikoPikoBlogの設定内容を設定例としてご紹介します。

1つ目
設定項目設定内容
30
時間3
*
*
曜日*
コマンド/usr/bin/php7.2 /home/poppink/pikopiko.blog/public_html/backup/backup_database.php
コメント
2つ目
設定項目設定内容
35
時間3
0
*
曜日*
コマンド/usr/bin/php7.2 /home/poppink/pikopiko.blog/public_html/backup/backup_webdata.php
コメント

分・時間・日・月は先頭に0をつけない数字にしてください。
曜日は0が日曜日、1が月曜日で、あとは数字を一つずつ足していって最後の土曜日が6です。
*は全てという意味です。

例として挙げているコマンドには/usr/bin/php7.2とありますが、phpのバージョンに応じて変更してください。
プログラムを走らせたいファイルはルートパスで指定してください。
ルートパスについてわからないかたは、サーバー環境変数の$_SERVER[“DOCUMENT_ROOT”]を使って確認することが出来ます。

勝手にプログラムを動かされることはないの?

勝手にプログラムを動かされることはあります。
対策としては、backup_database.phpとbackup_webdata.phpのファイル名を変更した上で、TransferBackUPFiles.phpの5行目と8行目の検索ファイル名を変更+Cronのコマンドを変更+.htaccessにファイル一覧禁止の記述をしてファイル名を特定させないようにすることです。

この文章を書いているときに、私は上記の対策をしました。
探せば他にも色々な対策方法があると思いますが、今の私ではこれぐらいしか思いつきません。

Dropboxにメイン同期しているPCの保存容量をバックアップファイルが圧迫してきたらどうすれば良いの?

PCのメモリが多いものに買い替えるか、Dropboxのスマートシンクで切り抜けるしかないでしょう。
Dropboxのスマートシンクは、使っていない古いファイルをクラウドに保存して、PC上ではショートカットキーみたいなものに変えてくれるので、徐々にメモリを開放してくれます。

手始めにスマートシンクを試してから、PCの買い替えを検討してみても良いのではないでしょうか。
ただし、今回のシステムを使うと古いバックアップファイルは削除されて新しいバックアップファイルが残るので、スマートシンクによる効果はほとんど期待できないと思っておいてください。


Cronを使って自作バックアップファイルをDropboxに自動転送できるようになりましたか?
私が作った自動バックアップシステムの大まかな仕組みを、以下にもう一度ご説明します。

  1. Dropbox APIの利用者登録をしてアクセストークンを取得する。
  2. WEBサーバー上に所定のファイルとフォルダーを作成して、一定の情報を変数に代入する。
  3. 自動バックアップ⇒Dropboxに自動転送するプログラムを走らせる時間のCron設定をする。

Dropbox APIに指示を出す方法さえわかれば、phpに限らずどの言語でも実装可能なはずです。
興味がある方は、ぜひ別の言語でも試してみてください!

以上、Cronを使って自作バックアップファイルをDropboxに自動転送する方法、でした。