ダウンロード

概要

WordPress BackWPupでバックアップされたzipファイルをローカルPCにダウンロードするスクリプトです。

コードについて

コードは AI (Gemini 2.5 Flash) が生成したものです。中身を確認しましたが、ほぼ完璧なので、修正せずそのまま載せています。プロンプトも添付しましたので、お好みでカスタマイズできます。

使い方

--- AI 生成 ここから ---

サーバー側スクリプト(backwpup-download.php)

このPHPスクリプトは、セキュリティのため、特定のアクセスキーがPOSTで提供された場合にのみ機能します。$secret_key、$backup_dir、$log_file は環境に合わせて必ず変更してください。

ローカル側スクリプト(PowerShell)

このPowerShellスクリプトは、サーバー上のbackwpup-download.phpにアクセスし、差分ダウンロードを実行します。

実行方法

PowerShellスクリプトを download-backups.ps1 として保存し、以下のように実行します。

.\download-backups.ps1 -TargetUrl "https://example.com/backwpup-download.php" -AccessKey "YOUR_SECURE_ACCESS_KEY" -DownloadPath "C:\Backups\WordPress"

スケジュール設定

このPowerShellスクリプトを毎日自動で実行するには、タスクスケジューラ(Windows)または cron(macOS/Linux)を使用してください。

--- AI 生成 ここまで ---

バッチファイル(.bat または .cmd)から実行する場合は以下のように指定します。
powershell.exe -NoProfile -ExecutionPolicy Bypass -File "C:\Scripts\download-backups.ps1" "https://www.example.com/backwpup-download.php" "YOUR_SECURE_ACCESS_KEY" "C:\Backups\WordPress"

セキュリティのため、PHPファイルはランダムな名前に変更することをお勧めします。
例:backwpup-download.php → download-k8u4dv.php

外部からのアクセスでファイル名が一覧表示されないことを確認してください。
例:https://www,example.com/folder1/backwpup-download.php に保存した場合、https://www,example.com/folder1/ でファイル名が表示されないことを確認する。

転送には https(暗号化通信)を利用してください。http で転送するとアクセスキーやデータベースパスワード、コンテンツ内容などが第3者に読み取られる可能性があります。

CDNを利用している場合はスクリプトをキャッシュ対象から除外する設定をするか、別のCDNを利用していないドメインにphpスクリプトを配置してください。そのまま利用すると、キャッシュされた内容を第3者に読み取られる可能性があります。

プロンプトをカスタマイズして AI にコード書かせた場合は、必ず内容を確認してください。AIが生成したコードは、セキュリティの考慮が欠けていることが多く、そのまま利用すると重大なセキュリティ事故に繋がることがあります。

レンタルサーバー会社によって転送速度に制限が設けられている場合があるようです。共有サーバーを多くの人に安価に快適に提供するために必要な制限と思われます。

ライセンス

The MIT License

Copyright 2025 North Innovations 髙橋一浩

Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

連絡先

takahashi@north-innovations.com

変更履歴

2025-11-10 ver.1.0
・初版

プロンプト

https://gemini.google.com/

以下のようなツールを作成してください。

目的:
WordPress の BackWPup ツールでバックアップされたファイルを毎日自動でローカルPCにダウンロードする。

サーバー:
backwpup-download.php ファイルを置く。以下の機能を持つ。
引数をPOSTで受け取り、結果を返す。
・アクセスキー
・コマンド

1.list コマンド
BackWPup のバックアップディレクトリの zip ファイルの一覧を返す。

2.get コマンド
BackWPup のバックアップディレクトリの zip ファイルをダウンロードする。

ローカル:
PowerShell スクリプトを置く。
引数で以下を受け取る。
・backwpup-download.php の URL
・アクセスキー
・ダウンロード先のパス

backwpup-download.php にアクセスし、以下のことを行う。
1.listコマンドでサーバーにあるファイルの一覧を取得する。
2.ローカルディレクトリのファイル一覧を取得する。
3.サーバー上のみにあるファイルをgetコマンドでダウンロードする。

サーバー

PHP
<?php
// 環境に合わせて以下の値を変更してください
$secret_key = 'YOUR_SECURE_ACCESS_KEY'; // PowerShellスクリプトと一致させる秘密鍵
$backup_dir = '/path/to/your/wordpress/wp-content/uploads/backwpup-xxxxxxxx-backups/'; // BackWPupのバックアップディレクトリへのフルパス
$log_file = '/path/to/your/log/backwpup_download.log'; // ログファイルのパス

// POSTリクエストの検証
if (!isset($_POST['access_key']) || $_POST['access_key'] !== $secret_key || !isset($_POST['command'])) {
    http_response_code(403);
    echo "Error: Access Denied or Invalid Request.";
    exit;
}

$command = $_POST['command'];

// ログ関数
function write_log($message) {
    global $log_file;
    $timestamp = date('Y-m-d H:i:s');
    file_put_contents($log_file, "[$timestamp] $message\n", FILE_APPEND);
}

// BackWPupのzipファイルのみをフィルタリング
function get_zip_files() {
    global $backup_dir;
    if (!is_dir($backup_dir)) {
        write_log("Error: Backup directory does not exist: $backup_dir");
        return [];
    }
    
    // ディレクトリ内のすべてのファイルを取得
    $files = @scandir($backup_dir);
    if ($files === false) {
        write_log("Error: Could not scan backup directory: $backup_dir");
        return [];
    }

    $zip_files = [];
    foreach ($files as $file) {
        // .zipで終わり、かつ、ファイルであること、ディレクトリではないことを確認
        if (preg_match('/\.zip$/i', $file) && is_file($backup_dir . $file)) {
            $zip_files[] = $file;
        }
    }
    return $zip_files;
}

switch ($command) {
    case 'list':
        // 1. list コマンド: zipファイルの一覧を返す
        $zip_files = get_zip_files();
        header('Content-Type: application/json');
        echo json_encode(['status' => 'success', 'files' => $zip_files]);
        write_log("LIST command executed. Found " . count($zip_files) . " files.");
        break;

    case 'get':
        // 2. get コマンド: 指定されたファイルをダウンロードさせる
        if (!isset($_POST['filename']) || empty($_POST['filename'])) {
            http_response_code(400);
            echo "Error: 'filename' parameter is required for 'get' command.";
            write_log("GET command failed: 'filename' parameter missing.");
            exit;
        }

        $filename = basename($_POST['filename']); // basenameでディレクトリトラバーサル攻撃を防止
        $filepath = $backup_dir . $filename;

        // ファイルの存在と安全性の確認
        if (!in_array($filename, get_zip_files())) {
            http_response_code(404);
            echo "Error: File not found or is not a valid backup file: $filename";
            write_log("GET command failed: File not found or invalid: $filename");
            exit;
        }

        // ファイルのダウンロード処理
        if (is_readable($filepath)) {
            header('Content-Description: File Transfer');
            header('Content-Type: application/zip');
            header('Content-Disposition: attachment; filename="' . $filename . '"');
            header('Content-Transfer-Encoding: binary');
            header('Expires: 0');
            header('Cache-Control: must-revalidate');
            header('Pragma: public');
            header('Content-Length: ' . filesize($filepath));
            ob_clean();
            flush();
            readfile($filepath);
            write_log("GET command executed. File downloaded: $filename");
            exit;
        } else {
            http_response_code(500);
            echo "Error: Could not read file: $filename";
            write_log("GET command failed: Could not read file: $filename");
            exit;
        }

    default:
        http_response_code(400);
        echo "Error: Invalid command.";
        write_log("Invalid command received: $command");
        break;
}

?>

クライアント

PowerShell
<#
.SYNOPSIS
  WordPress BackWPupのバックアップファイルをサーバーからローカルにダウンロードします。

.DESCRIPTION
  listコマンドでサーバー上のファイル一覧を取得し、getコマンドでローカルに存在しないファイルをダウンロードします。

.PARAMETER TargetUrl
  backwpup-download.php のフルURL (例: https://example.com/backwpup-download.php)

.PARAMETER AccessKey
  サーバー側PHPスクリプトで設定した秘密のアクセスキー

.PARAMETER DownloadPath
  バックアップファイルを保存するローカルディレクトリのパス
#>
param(
    [Parameter(Mandatory=$true)]
    [string]$TargetUrl,

    [Parameter(Mandatory=$true)]
    [string]$AccessKey,

    [Parameter(Mandatory=$true)]
    [string]$DownloadPath
)

# ダウンロードディレクトリの確認と作成
if (-not (Test-Path -Path $DownloadPath -PathType Container)) {
    Write-Host "ダウンロードディレクトリ $DownloadPath が存在しないため作成します。"
    try {
        New-Item -Path $DownloadPath -ItemType Directory | Out-Null
    } catch {
        Write-Error "ダウンロードディレクトリの作成に失敗しました: $($_.Exception.Message)"
        exit 1
    }
}

# --- 1. サーバー上のファイル一覧を取得 (listコマンド) ---
Write-Host "サーバー上のファイル一覧を取得中..."
try {
    $ListBody = @{
        'access_key' = $AccessKey;
        'command'    = 'list'
    }

    $ListResponse = Invoke-RestMethod -Uri $TargetUrl -Method Post -Body $ListBody -ContentType 'application/x-www-form-urlencoded'

    if ($ListResponse.status -ne 'success') {
        Write-Error "サーバーからのリスト取得に失敗しました: $($ListResponse | ConvertTo-Json -Depth 1)"
        exit 1
    }

    $ServerFiles = $ListResponse.files
    Write-Host "サーバー上で合計 $($ServerFiles.Count) 個のファイルが見つかりました。"
} catch {
    Write-Error "listコマンドの実行中にエラーが発生しました: $($_.Exception.Message)"
    exit 1
}

# --- 2. ローカルディレクトリのファイル一覧を取得 ---
Write-Host "ローカルディレクトリのファイル一覧を取得中..."
$LocalFiles = Get-ChildItem -Path $DownloadPath -Filter "*.zip" | Select-Object -ExpandProperty Name
Write-Host "ローカルで合計 $($LocalFiles.Count) 個のファイルが見つかりました。"


# --- 3. 差分をダウンロード (getコマンド) ---
$FilesToDownload = $ServerFiles | Where-Object { $_ -notin $LocalFiles }

if ($FilesToDownload.Count -eq 0) {
    Write-Host "ダウンロードすべき新しいファイルはありませんでした。"
    exit 0
}

Write-Host "新しくダウンロードすべきファイルは $($FilesToDownload.Count) 個です。"

$FilesToDownload | ForEach-Object {
    $FileName = $_
    $LocalFilePath = Join-Path -Path $DownloadPath -ChildPath $FileName

    Write-Host "-> $FileName をダウンロード中..."

    try {
        $GetBody = @{
            'access_key' = $AccessKey;
            'command'    = 'get';
            'filename'   = $FileName
        }

        # ダウンロードを実行
        Invoke-WebRequest -Uri $TargetUrl -Method Post -Body $GetBody -OutFile $LocalFilePath -ContentType 'application/x-www-form-urlencoded'

        # ファイルサイズチェックなど、必要に応じて追加の検証をここで行うことができます。
        
        Write-Host "   $FileName のダウンロードが完了しました。"
    } catch {
        Write-Error "ファイルのダウンロード中にエラーが発生しました ($FileName): $($_.Exception.Message)"
        # エラーが発生しても処理を続行
    }
}

Write-Host "すべての処理が完了しました。"