プログラミング

宅ふぁいる便のような機能をPHPシングルファイルで実現したい

2022年8月5日

PHP: POST メソッドによるアップロード - Manual https://www.php.net/manual/ja/features.file-upload.post-method.php

PHPのドキュメントをほぼほぼ流用してそれっぽいものを作ってみた。

  • アップロードしたらuniqid()なダウンロードURLが生成される
  • ダウンロードと同時にサーバー側のファイルを自動的に削除

URL誤送信に気づいたらすぐに自分でダウンロードすればOK。

会員登録、2GB容量制限、保持期限、ダウンロード回数、パスワード、複数ファイル、短縮URL、メール送信、受け取り確認、メモ機能、通報機能、確認機能、ウィルスチェック、個別orまとめてダウンロード、D&D、通知機能、利用規約、約款、お問い合わせ対応、ログインなどは一切実装する予定なし、いくらでも実装できるけど。

php.iniで自由に容量制限できる、apache側とかnginx側も制限あるからそのへんも変更できないとダメなんだけど、レンサバの仕様次第、試してみないとなんともいえない。

見込み客だったりお問い合わせの回答としてそこそこ大きいファイルを送らないといけないんだけど...

  • 誰か他のひとが管理してるサーバーにアップロードするのはなんか嫌だなあ
  • メールは容量制限に引っかかるし
  • OneDriveは社内だけで使いたい
  • そもそもMicrosoft 365のアカウント作ってくださいっていいづらい
  • Dropboxは個人用
  • Boxは一応あるけど超お得意のA社とシェアしてる
  • backlogだったりGithubで共有するのもなんかちがう
  • このお客さん、永続的なつながりがあるかはまだ未知数なんだよな
  • そもそもメールアドレス知らないし
  • メールじゃなくてTwitterとかfacebookとかLINEで送りたいんだよなあ
  • 機能は最低限でいいから自分のサーバーだったりレンサバだったりVPSだったりで使える簡単に理解できる軽量なシステムがあればいいんだけど...
  • PHPシングルファイルで置くだけで動いてくれたりしないかな?
  • 用が済んだら気軽に削除できるしセキュアだよね
  • フレームワークとかデータベースとかそういうのはセットアップとかセキュリティとかめんどくさいからいらないんだ
  • そこまでのことじゃないんだ
  • ただただPHPシングルファイルを置くだけですぐ使えるようなやつがほしい
  • あの懐かしいKENT WEBの掲示板のような感じの...というニーズを満たしたい。

と、思って作ってはみたものの...

なんかgithubとかに無料公開して使えるひとは使ってねっていう形でやってみるのもいいんだけどなんかいまいちかもしれない、周知できないし使ってくれなさそう。

DonateとかPatreonとかコーヒー1杯とかREADME.mdに貼ってみてもいいかもしれないけど、開発者は見てくれるけど一般人というか実際のお客さんは見てくれなさそう。

例えば動画編集者とかカメラマンとかがお客さんだったとしたら...?

シングルファイルだとミドルウェアやフレームワークをインストールしなくてよくてPHPだけ動けばいいからデプロイが簡単でPHPだったらだいたいどこのサーバーでも動くし誰でも設置できるし便利だと思ってたけどgit cloneだったりcurlだったりffftpとかでレンサバに設置してもらってもいいかもしれない。

wordpressとかそうやってみんな設置してるし、自分が思ってるほどシングルファイルであることの価値はないかもしれない。

というか、こういう作業一切やりたくないんだよなきっと。本質的な作業は撮影や編集だから、それ以外のこと、例えばサーバーのことなんかはカネ払ってでも業者にやってもらいたいっていうひとが大半だと思う。

OSセキュリティアップデート、Apacheバージョンアップ、PHPバージョンアップ、SSL更新、脆弱性対応とか面倒だしやりたくないよな、そもそもできないだろうし、機能だけ使いたい、メンテナンスなんてしたくない、やりたくないからカネを払う。

きっちりお金にするために、満足して使ってもらえるようにするためにどうすればいいのか、サーバー設置代行かな?マルチテナント的な感じでいいよね、あとは月額課金する感じ?Herokuボタン的なのとか?AWSでEC2にOSSをデプロイして利用料徴収するのどういうしくみなんだろう、マーケットプレイス?そういうのでもいいかもしれない。

自分のサーバーに設置したいっていう欲求自体が間違ってるかもしれないけど。

SaaSでいいよね、うーん、やっぱりニッチかな、でも誤送信がなあ、設定ミスで機密情報を暴露しちゃったり、SaaSの情報漏えい事故が心配だし、切り分けたい、限定したい、刺さるひとには刺さる?なんともいえない。

宅ふぁいる便の競合を調べてたら100ユーザー月額30000円、利用歴12年みたいな事例があった。

月額30000x12ヶ月=360000円x12年=432万円

もし10社、100社、1000社と契約が取れたとしたら

月額30000x12ヶ月=360000円x10社=360万円

月額30000x12ヶ月=360000円x100社=3600万円

月額30000x12ヶ月=360000円x1000社=3億6千万円

100社くらいはニーズありそうかな?

ソースコードを公開するのでご自由にお使いください。

サーバーとかセキュリティとかアップデートとか面倒だよ、でもこの機能が必要だし使いたい、お金払うからその辺面倒見てくれない?ってかたがいらっしゃいましたら、月額30000円で対応しますのでどうぞよろしくお願いいたします。お問い合わせはkanetann at gmail.comまで。

; 1GB + 4096 = (1024x1024x1024) + 4096
; MAX_FILE_SIZEの箇所でini_get('upload_max_filesize')しているのでバイトで記述すること
upload_max_filesize = 1073745920
post_max_size = 1073745920
memory_limit = 1073745920
<?php
$response = '';
// Basic Authentication
// switch (true) {
//   case !isset($_SERVER['PHP_AUTH_USER'], $_SERVER['PHP_AUTH_PW']):
//   case $_SERVER['PHP_AUTH_USER'] !== 'admin':
//   case $_SERVER['PHP_AUTH_PW']   !== '<YOUR PASSWORD>':
//     header('WWW-Authenticate: Basic realm="Enter username and password."');
//     header('Content-Type: text/plain; charset=utf-8');
//     die('Login Error');
// }
// 前処理 /var/www/html/phpFileTransferにindex.phpがあり、ドキュメントルート外にuploadsディレクトリを作ってるつもり
$uploads_directory = "../../uploads/";
if (is_dir($uploads_directory) === false) {
  mkdir($uploads_directory, 0700);
}
// アップロード処理
if ($_FILES) {
  $file = sprintf("%s_%s", uniqid('', true), basename($_FILES['userfile']['name']));
  if (move_uploaded_file($_FILES['userfile']['tmp_name'], $uploads_directory . $file)) {
    $response = sprintf(
      "%s%s%s?file=%s",
      empty($_SERVER['HTTPS']) ? 'http://' : 'https://',
      $_SERVER['HTTP_HOST'],
      $_SERVER['REQUEST_URI'],
      htmlspecialchars($file, ENT_QUOTES, 'UTF-8')
    );
  } else {
    // PHP: Error Messages Explained - Manual https://www.php.net/manual/en/features.file-upload.errors.php
    $phpFileUploadErrors = array(
      0 => 'There is no error, the file uploaded with success',
      1 => 'The uploaded file exceeds the upload_max_filesize directive in php.ini',
      2 => 'The uploaded file exceeds the MAX_FILE_SIZE directive that was specified in the HTML form',
      3 => 'The uploaded file was only partially uploaded',
      4 => 'No file was uploaded',
      6 => 'Missing a temporary folder',
      7 => 'Failed to write file to disk.',
      8 => 'A PHP extension stopped the file upload.',
    );
    $response = $phpFileUploadErrors[$_FILES['userfile']['error']];
  }
}
// ダウンロード処理
if (isset($_GET['file'])) {
  $path = $uploads_directory . basename($_GET['file']);
  if (file_exists($path)) {
    header('Content-Type: application/octet-stream');
    header('Content-Length: ' . filesize($path));
    header('Content-Disposition: attachment; filename*=UTF-8\'\'' . rawurlencode($path));
    readfile($path);
    unlink($path);
  }
  header('Location: /phpFileTransfer/');
  exit;
}
?>
<!doctype html>
<html lang="en" class="h-100">

<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.2.0/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-gH2yIJqKdNHPEq0n4Mqa/HGKIhSkIHeL5AyhkYV8i59U5AR6csBvApHHNl/vI1Bx" crossorigin="anonymous">
  <title>phpFileTransfer</title>
</head>

<body class="d-flex h-100 text-center">

  <div class="container d-flex w-100 h-100 mx-auto flex-column">
    <header class="mb-auto">
      <h1 class="display-1 float-md-start">phpFileTransfer</h1>
    </header>

    <main>
      <form enctype="multipart/form-data" action="index.php" method="POST">
        <input type="hidden" name="MAX_FILE_SIZE" value="<?= ini_get('upload_max_filesize') === '2M' ? 2097152 : ini_get('upload_max_filesize') ?>"   />
        <input name="userfile" type="file" class="form-control" />
        <input type="submit" value="Upload" class="btn btn-primary" />
        <div>
          <label for="response" class="form-label">Download URL</label>
          <input type="text" name="response" id="response" value="<?= $response ?>" class="form-control">
        </div>
      </form>
    </main>

    <div class="mt-auto">
      <p></p>
    </div>

  </div>
  <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.2.0/dist/js/bootstrap.bundle.min.js" integrity="sha384-A3rJD856KowSb7dwlZdYEkO39Gagi7vIsF0jrRAoQmDKKtQBHUuLZ9AsSv4jD4Xa" crossorigin="anonymous"></script>
</body>

</html>

2022/08/08 追記:なんかいろいろ修正して最新版をgithubにあげておきました。

kanetann/phpFileTransfer https://github.com/kanetann/phpFileTransfer

Herokuにデプロイできるようにapp.jsonを書いてデプロイしてみたけど、Herokuはファイルアップロードを受け付けておらず、S3と連携できるようにしないといけないらしく断念(Herokuのセンスのよさ)。

コードはいくらでも書くつもりあるし新しい知識を身につけてできることが増えるのは楽しいんだけどお金にするのは難しいね。

-プログラミング
-