ユーザーアカウント不要で手軽に画像をアップロード、共有、表示できるオンライン画像ホスティングサービスです。
https://pix-pocket.yuki-gakiya.com
Pix Pocketは、ユーザーアカウント不要の画像共有サービスです。
TOPページで「ファイルを選択」ボタンをクリックし、アップロードしたい画像を指定します。その後、POSTボタンを押すことで、選択した画像がサーバーにアップロードされます。
アップロードが完了すると、モーダルが表示され、一意の共有用URLと削除用URLが提供されます。
共有用URLは他のユーザーと画像を共有するための手段で、このURLにアクセスすることで自身がアップロードした画像と閲覧数を確認できます。
さらに、削除用URLへのアクセスにより、共有用URLを削除できるボタンが表示され、ユーザーは簡単に不要な共有を取り消すことができます。なお、30日間アクセスがない画像を自動的にサーバーから削除する仕組みを導入し、データの整理を行っています。
- 画像アップロード
- URL作成
- 閲覧数カウンター
- 手動での画像削除
- 一定期間アクセスがない画像の自動削除
- エラーハンドリング
Pix Pocketの発想は、Text Snippetterのプロジェクトで得た経験に基づいています。Text Snippetterでは3層アーキテクチャやサーバーサイドレンダリングを利用して、テキストを共有できるサービスを構築しました。
この成功を受けて、同じコンセプトを拡張し、画像のアップロード、共有、表示が可能なPix Pocketを開発しました。
ユーザーアカウントの作成を必要とせず、かつアップロードしたデータを柔軟に削除できるサービスを提供することを目標としました。
また、サーバーの効率的な管理を実現するためにcronの活用も計画しました。
項目 | 内容 |
---|---|
使用言語 | HTML, CSS, Javascript |
CSSフレームワーク | Pico.css, Flexbox Grid |
CSSアニメーション | Animista |
項目 | 内容 |
---|---|
使用言語 | PHP |
データベース | MySQL |
ジョブ管理ツール | cron |
Webサーバー | NGINX |
サーバー | Amazon EC2 |
SSL/TLS証明書更新 | Certbot |
2023年の11月20日から10日間かけて開発しました。
当プロジェクトではマイグレーションを除いた場合、imagesテーブルのみを使用しています。
以下はimagesテーブルのER図です。
スケーラビリティを確保するために、画像の保存方法に工夫を凝らしました。
具体的な手法としては、アップロードされた画像に対してランダムなハッシュを生成し、そのハッシュから得られる最初の2文字を用いてディレクトリを作成します。
まず、画像のファイル名生成には以下の方法を採用しました。
hash('sha256', uniqid(mt_rand(), true)) . '.' . $extension
この手法では、乱数をもとにして生成されたユニークな識別子(uniqid)を SHA-256 ハッシュ関数にかけ、それにファイルの拡張子をつけることで、一意でセキュアなファイル名が得られます。
そして、生成されたファイル名の最初の2文字を取り出し、それを元にしてディレクトリを作成します。
例えば、アップロードされた画像が 4eb240a8bef5c422ab44717cb80e5849d77a.png
であれば、これにより以下のようなディレクトリ構造が生成されます。
uploads/
|-- 4e/
| |-- 4eb240a8bef5c422ab44717cb80e5849d77a.png
このハッシュをベースとしたディレクトリ構造により、ファイルの分布を均等にし、スケーラビリティの確保を実現しています。
システムのストレージを効果的に管理するために、cronを活用して30日間アクセスのない画像を自動的に削除する機能を導入しました。
最初に、dac(DailyAccessCheck)という独自コマンドを作成しました。
このコマンドの作成により、ターミナルにphp console dac
と入力することで、30日間アクセスのないデータと画像ファイルが削除されるようになりました。
削除されたデータに関する情報はDelete {filename}
がログとして標準出力され、30日間アクセスのないデータが1つもない場合は、No data exceeds 30 days.
という形でログとして標準出力されます。 これにより、実行の結果を簡潔かつ明瞭に確認できるようにしました。
cronで定期実行するために、cron/daily_access_check.phpを作成し、以下のようにcronに登録しました。
0 0 * * * /path/to/php /path/to/cron/daily_access_check.php >> /path/to/cron/log.txt 2>&1
これにより、毎日定時に30日間アクセスのないデータが削除され、同時に詳細なログがcron/log.txtに出力されます。
なお、cron/log.txtは.gitignoreに登録しており、GitHub上では閲覧できません。
log.txtの出力例
log.txtの出力例は以下の通りです。
<!-- 30日間アクセスがなかったデータが存在した場合 -->
2023-12-29 00:00:00
Array
(
[0] => Starting an access check.......
[1] => Delete /path/to/image
[2] => Access check complete.
)
<!-- 削除するデータがなかった場合 -->
2023-12-30 00:00:00
Array
(
[0] => Starting an access check.......
[1] => No data exceeds 30 days.
)
<!-- エラーが発生した場合 -->
2023-12-31 00:00:00
PHP Warning: ...
cronを導入するにあたって、以下のようにテストを行いました。
ローカル環境での手動テスト
ローカル環境で以下のようなコマンドを実行して、コマンドのロジックに誤りがないか確認しました。
php /path/to/cron/daily_access_check.php >> /path/to/cron/log.txt 2>&1
エラーがないことを確認した後、以下をcronに登録しました。 そして、テストデータを用意して、データが1分後に削除されることを確認しました。
* * * * * /path/to/php /path/to/cron/daily_access_check.php >> /path/to/cron/log.txt 2>&1
本番環境にデプロイ後ログの確認
プロジェクトを本番環境にデプロイした後、cronを登録しました。その後、cron/log.txtを確認して、システムが正常に動作していることを確認しました。
ユーザーがアップロードした画像の検証を行い、条件に合わない場合には適切なエラーメッセージを表示するようにしました。
このプロジェクトでは、まず対応している画像形式を「png, jpeg, gif」に制限し、一度のアップロード容量を5MB、1日のアップロード総容量も5MBに設定しました。 ユーザーがこれらの条件を満たさない画像をアップロードした場合、適切なアラートが表示されます。
「1日にアップロードできる総容量」についてはIPアドレスごとに制限をかけ、悪意ある利用を防ぐための対策を実施しました。
また、エラーメッセージの表示はユーザーが画像をアップロードした場合だけでなく、データベースに画像を保存する際やデータの取得に失敗した場合にも行うようにし、ユーザーが発生したエラーに対処しやすいようにしました。
現段階では、画像をアップロードすると、共有用と削除用のURLがモーダルで表示されます。ただし、モーダルを閉じると、これらのURLを後から確認する方法がありません。
これを解決するために、ユーザーアカウントと関連付けて、以前にアップロードした画像の共有用URLや削除用URLをユーザーがいつでも確認できるように改良を進める予定です。
ユーザーがサイトにアクセスした全員が閲覧できるページを作成し、そこに画像を投稿したり、画像にコメントや投票をしたりできるコミュニティ機能を追加したいです。