1
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

【PHP】ファイルサーバーをPHPで作ってみた

Last updated at Posted at 2023-11-29

こんにちは、技術猫です。
(初投稿です)

ApacheとPHP(とTailwind CSS)でファイルサーバーを作ってみました。
この記事では、ログイン機能に必要なJSONファイルの読み取りの方法や、
ファイルのアップロード機能の実装方法などを紹介します。
そしておまけで、PHPでのコマンドの実行方法などを紹介します。

コード内のコメントアウトされてる部分を読むと意味がわかるよ!

この記事で紹介するコードの一部は、セキュリティが万全ではない可能性があります。
社外などに公開することは推奨しません!

ソース

作成した全部のソースコードはGitHubのreleaseにてMITライセンスで配布しています。

一応ApacheとPHPがあれば使えます。(テスト環境はmacOS)

ログイン機能

ログイン機能は、最初はパスワードハッシュをPHP内で書いていましたが、
後から出てくる「ユーザー追加機能」という機能が必要なため、
Jsonファイルに保存することになりましたが...

早速間違えた!

プログラミング初心者なので、JSONファイルをPHPで読み込む際に、変な書き方をしていました。

users.json

{"users":[
    {
        "username":"admin",
        "password":"$2y$10$3dmC2Lq8r/9B8F83NrLqMulwkwuk.QgJsvQXXumwX83k6mQzmjQP2"
    }
]}

login.php (修正前)

<?php
    require_once __DIR__ . '/functions.php';
    require_unlogined_session();
    function get_password($username) {
        $json = file_get_contents('http://192.168.1.100/tCloud/users.json');
        $datas = json_decode($json, true);
        $name = $datas['users']
        return $name[$username]['password']; 
        # users.jsonの中でも、"users"の中にはusernameはなく、"users"の1番目の中にusernameがある。
    }
    $username = filter_input(INPUT_POST, 'username');
    $password = filter_input(INPUT_POST, 'password');
    if ($username == "rsa"){

        header("Location: rsa.php");
        exit;

    }
    if ($_SERVER['REQUEST_METHOD'] === 'POST') {

        if (password_verify($password,get_password($username))){ # なので、ここでエラー。
            session_regenerate_id(true);
            $_SESSION['username'] = $username;
            header('Location: /tCloud/');
            exit;
        } else {
            http_response_code(403);
        }
    }
    header('Content-Type: text/html; charset=UTF-8');
# この先省略

Microsoft Copilot(Bing)に教えてもらった修正点を使って修正した後はこちら:

login.php (修正後)

<?php
    require_once __DIR__ . '/functions.php';
    require_unlogined_session();
    function get_password($username) {
        $json = file_get_contents('http://192.168.1.100/tCloud/users.json');
        $datas = json_decode($json, true);
        foreach ($datas['users'] as $name) {
            if ($name['username'] == $username) {
                return $name['password']; #ちゃんと全部読み取ってみて
            }
        }
        return null; # なかったらNull
    }
    $username = filter_input(INPUT_POST, 'username');
    $password = filter_input(INPUT_POST, 'password');
    if ($username == "rsa"){
        header("Location: rsa.php");
        exit;
    }
    if ($_SERVER['REQUEST_METHOD'] === 'POST') {
        if (password_verify($password,get_password($username))){
            session_regenerate_id(true);
            $_SESSION['username'] = $username;
            header('Location: /tCloud/');
            exit;
        } else {
            http_response_code(403);
        }
    }
    header('Content-Type: text/html; charset=UTF-8');

# この先省略

という感じです。
何番目かを指定する必要があるので、foreachにする必要があることにこの時気がつきました。

ストレージのアップロード機能

アップロードは一つずつ調べて完成しました。
ユーザーごとのストレージがあるのと、共有ドライブがある感じです。

upload.php (共有ドライブ)

<?php

require_once __DIR__ . '/../functions.php';
require_logined_session();

header('Content-Type: text/html; charset=UTF-8');

?>

<!DOCTYPE html>
<html lang="ja">
    <head>
        <meta charset="UTF-8" />
        <meta name="viewport" content="width=device-width, initial-scale=1.0" />
        <title>Share File</title>
        <link rel="stylesheet" href="/dist/output.css" />
    </head>

    <body class="text-black bg-white">
        <div class="flex flex-col h-screen">
        <div class="">
            <font color="blue">
                <a href="#" onclick="history.back()"> < back </a>
            </font>
        </div>
            <div>
                <div class="flex justify-center mt-20">
                    <div class="w-9/12 border rounded-3xl bg-gray-200 dark:bg-gray-200">
                        <div class="my-16 text-center">

                            <img src="/link.png" style="display: block; margin: auto;">
                            <br><br>

                            <h2 class="text-xl">Upload</h2>

                            <font color="orange">

                            <?php
                                $temporary_file = $_FILES['user_file_name']['tmp_name']; # 一時ファイル名
                                $true_file = $_FILES['user_file_name']['name']; # 本来のファイル名

                                # is_uploaded_fileメソッドで、一時的にアップロードされたファイルが本当にアップロード処理されたかの確認
                                if (is_uploaded_file($temporary_file)) {
                                    if (move_uploaded_file($temporary_file , $true_file )) {
                                        echo "Upload completed.
                                        The file is located at the following URL:<br>
                                        <a href='http://192.168.1.100:8080/tCloud/files/" . $true_file . "'>http://192.168.1.100:8080/files/" . $true_file . "</a>";
                                    } else {
                                        echo "Upload failed.";
                                    }
                                } else {
                                    echo "Upload failed.";
                                }
                            ?>

                            </font>

                            <br><br>
                            <form enctype="multipart/form-data"  action="upload.php" method="POST">
                              <input type="hidden" name="name" value="value" />
                              <input name="user_file_name" type="file" />
                              <input type="submit" value="Upload" />
                            </form> 
                        </div>
                    </div>
                </div>
            </div>
        </div>
    </body>
</html>

おまけ: Admin Console

そしておまけ機能として、Admin Consoleというものを作りました。
ユーザーの追加を管理者ができるようなツールです。

tCloud/admin/add.php (Admin Console)

<?php

require_once __DIR__ . '/../functions.php';
require_logined_session();

$username = $_SESSION['username'];

if ($username == "admin"){

} else{
    header("Location: /403.php");
    exit();
}

header('Content-Type: text/html; charset=UTF-8');

?>

<!DOCTYPE html>
<html lang="ja">
    <head>
        <meta charset="UTF-8" />
        <meta name="viewport" content="width=device-width, initial-scale=1.0" />
        <title>Add User - tCloud</title>
        <link rel="stylesheet" href="/dist/output.css" />
    </head>
    <body class="text-black bg-white">
        <div class="flex flex-col h-screen">
        <div class="">
            <br>
        </div>
            <div>
                <div class="flex justify-center mt-20">
                    <div class="text-center">

                        <img src="/icon-128x128.png" style="display: block; margin: auto;">
                        <h1 class="text-5xl">tCloud</h1>
                        Admin Console
                        <br><br>
                        <form action="adduser.php" method="POST" class="mt-12 disabled:bg-slate-50 disabled:text-slate-500  disabled:shadow-none  invalid:text-pink-600  focus:invalid:ring-pink-500">
                            <div class="mb-3">
                                <input
                                    type="text"
                                    placeholder="ID"
                                    name="username"
                                    required
                                    class="w-80 focus:ring-2 focus:ring-blue-500 focus:outline-none appearance-none text-sm text-slate-900 placeholder-slate-400 rounded-md py-2 ring-1 ring-slate-200 shadow-sm"
                                />
                            </div>
                            <div class="mb-5">
                                <input
                                    type="text"
                                    placeholder=" PW"
                                    name="password"
                                    minlength="6"
                                    class="w-80 focus:ring-2 focus:ring-blue-500 focus:outline-none appearance-none text-sm text-slate-900 placeholder-slate-400 rounded-md py-2 ring-1 ring-slate-200 shadow-sm"
                                />
                            </div>
                            <input type="hidden" name="token" value="<?=h(generate_token())?>">
                            <button type="submit">
                                Create User
                            </button>
                        </form>

                    </div>
                </div>
            </div>
        </div>
    </body>
</html>

処理側は二つあって、

/tCloud/admin/adduser.phpと、
/tCloud/drive/adduser.phpがあります。

やっていることはコマンド実行とファイル作成です。

adminフォルダのadduser.phpは、

tCloud/admin/adduser.php (Admin Console)

<?php

    require_once __DIR__ . '/../functions.php';
    require_logined_session();

    $username = $_SESSION['username'];

    if ($username == "admin"){

    } else{
        header("Location: /403.php");
        exit();
    }

    $user = [
        'username' => $_POST['username'],
        'password' => password_hash($_POST['password'], PASSWORD_BCRYPT)
    ];

    $json = file_get_contents('http://192.168.1.100/tCloud/users.json'); # jsonを取得
    $data = json_decode($json, true); # jsonデータを配列へ
    $data['users'][] = $user; # jsonファイルの中の'users'の中を指定
    $json = json_encode($data); # 配列をjsonデータへ
    file_put_contents('../users.json', $json); # 'users'に追記

    header("Location: ../drive/adduser.php?username=" . $_POST['username']);
    # driveのadduser.phpに移動

?>

という感じです。
driveのadduser.phpに移動したら、以下のファイルが読み込まれます。

tCloud/admin/adduser.php (Admin Console)

<?php

    require_once __DIR__ . '/../functions.php';
    require_logined_session();

    $username = $_SESSION['username'];

    if ($username == "admin"){

    } else{
        header("Location: /403.php");
        exit();
    }

    exec("mkdir " . $_GET['username']); # ディレクトリ(フォルダ)作成
    exec("chmod 777 " . $_GET['username']); # 読み込み・書き込み権限つける
    exec("touch " . $_GET['username'] . "/index.php"); # index.php作成
    exec("chmod 777 " . $_GET['username'] . "/index.php"); # 読み込み・書き込み権限つける
    $file = fopen($_GET['username'] . "/index.php", "w");# ファイルを開く
    fwrite($file, '
    
    <?php

    require_once __DIR__ . "/../../functions.php";
    require_logined_session();
    
    $username = $_SESSION["username"];
    
    if ($username == "' . $_GET['username'] . '"){
    
    } else{
        header("Location: /403.php");
        exit();
    }
    
    header("Content-Type: text/html; charset=UTF-8");
    
    ?>
    
    <!DOCTYPE html>
    <html lang="ja">
        <head>
            <meta charset="UTF-8" />
            <meta name="viewport" content="width=device-width, initial-scale=1.0" />
            <title>Your Drive</title>
            <link rel="stylesheet" href="/dist/output.css" />
        </head>
    
        <body class="text-black bg-white">
            <div class="flex flex-col h-screen">
            <div class="">
                <font color="blue">
                    <a href="#" onclick="history.back()"> < back </a>
                </font>
            </div>
                <div>
                    <div class="flex justify-center mt-20">
                        <div class="w-9/12 border rounded-3xl bg-gray-200 dark:bg-gray-200">
                            <div class="my-16 text-center">
    
                              
                                <img src="/drive.png" style="display: block; margin: auto;">
                                <br><br>
    
                                <h2 class="text-xl">Upload</h2>
    
                                <form enctype="multipart/form-data"  action="upload.php" method="POST">
                                  <input type="hidden" name="name" value="value" />
                                  <input name="user_file_name" type="file" />
                                  <input type="submit" value="Upload" />
                                </form> 
    
                                <br><br>
    
                                <h2 class="text-xl">Files</h2>
    
                                <?PHP
                                  $dirpath = "./";
                                  $dirlist = dir($dirpath);
                                  while( $filename = $dirlist->read() ){
                                    if( (is_dir($filename) == false) && ($filename!=".." || $filename!= "." ) ){
                                      if ($filename !== "index.php" && $filename !== "upload.php"){
                                        print("<a href=\"/download.php?author=chii&path=" .  $filename . "\"><button>".$filename."</button></a><br>\n");
                                      }
                                    }
                                  }
                                  $dirlist->close();
                                ?>
                                
                            </div>
                        </div>
                    </div>
                </div>
            </div>
        </body>
    </html>
    
    
    
    '); # ファイルに書き込み
    fclose($file); # ファイルを閉じる

    exec("touch " . $_GET['username'] . "/upload.php"); # upload.phpを作成
    exec("chmod 777 " . $_GET['username'] . "/upload.php"); # 読み込み・書き込み権限をつける
    $file = fopen($_GET['username'] . "/upload.php", "w"); # upload.phpを開く
    fwrite($file, '
    
    <?php

require_once __DIR__ . "/../../functions.php";
require_logined_session();

header("Content-Type: text/html; charset=UTF-8");

$username = $_SESSION["username"];

if ($username == "' . $_GET['username'] . '"){

} else{
    header("Location: /403.php");
    exit();
}

?>

<!DOCTYPE html>
<html lang="ja">
    <head>
        <meta charset="UTF-8" />
        <meta name="viewport" content="width=device-width, initial-scale=1.0" />
        <title>Your Drive</title>
        <link rel="stylesheet" href="/dist/output.css" />
    </head>

    <body class="text-black bg-white">
        <div class="flex flex-col h-screen">
        <div class="">
            <font color="blue">
                <a href="#" onclick="history.back()"> < back </a>
            </font>
        </div>
            <div>
                <div class="flex justify-center mt-20">
                    <div class="w-9/12 border rounded-3xl bg-gray-200 dark:bg-gray-200">
                        <div class="my-16 text-center">

                          
                            <img src="/drive.png" style="display: block; margin: auto;">
                            <br><br>

                            <h2 class="text-xl">Upload</h2>

                            <font color="orange">

                            <?php
                                $temporary_file = $_FILES["user_file_name"]["tmp_name"]; 
                                $true_file = $_FILES["user_file_name"]["name"]; 

                                if (is_uploaded_file($temporary_file)) {
                                    if (move_uploaded_file($temporary_file , $true_file )) {
                                        echo $true_file . " is Uploaded";
                                    } else {
                                        echo "Upload failed";
                                    }
                                } else {
                                    echo "Upload failed";
                                }
                                var_dump($true_file);
                            ?>

                            </font>

                            <br><br>
                            <form enctype="multipart/form-data"  action="upload.php" method="POST">
                              <input type="hidden" name="name" value="value" />
                              <input name="user_file_name" type="file" />
                              <input type="submit" value="Upload" />
                            </form> 

                            <br><br>

                            <h2 class="text-xl">Files</h2>

                            <?PHP
                              $dirpath = "./";	
                              $dirlist = dir($dirpath);
                              while( $filename = $dirlist->read() ){
                                if( (is_dir($filename) == false) && ($filename!=".." || $filename!= "." ) ){
                                  if ($filename !== "index.php" && $filename !== "upload.php"){
                                    print("<a href=\"/download.php?author=chii&path=" .  $filename . "\"><button>".$filename."</button></a><br>\n");
                                  }
                                }
                              }
                              $dirlist->close();
                            ?>
                            
                        </div>
                    </div>
                </div>
            </div>
        </div>
    </body>
</html>


    
    '); # ファイルに書き込み
    fclose($file); # ファイルを閉じる

    header("Location: ../admin/index.php"); # 最初の画面に戻る

?>

ちょっと作るの難しかったです...

終わりに

どうだったでしょうか。
初投稿なのでまだ書き方など難しいですが、
JSONの読み取り方法など参考になれば幸いです。

GitHubのreleaseでも公開しているのでぜひみてみてください。
見てくれてありがとうございました。
それではまた他の記事で!

1
1
2

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?