#はじめに
Webサービス作るときにユーザーがアップロードした画像はどこに保管するのが適切なのでしょうか!?
Instagramとかpixivみたいなアップロードが前提のサービスだったり、そうでなくとも、プロフィール用に写真をアップロードすることも多いと思います。
トビデモ作った際に、ユーザーがアップロードした画像の保管方法に困りました。
画像保存しているパスがわかると、パスを辿って他の画像も見放題だよね!?なんか気持ち悪い。
と思い調べてみたのですが、ベストプラクティスみたいな記事もなかったので自分なりに調べて実装した方法についてまとめています。
#調査
世の中のwebサービスでは画像をどう処理しているか、いくつか確認してみました。
※元URLがわからないように一部IDなどを変えています。
###pixiv
イラストもアイコンも日時情報+UUIDで管理しているようですね。
毎日ディレクトリー増えていくと、5年で1825ディレクトリーになりますが、この辺の検索のパフォーマンスは
どう改善していくのか気になります。
イラスト:
https:// i.pximg.net/img-original/img/日時情報/05/19/日時情報/32/55/608090000_p0.jpg
アイコン:
https: //i.pximg.net/user-profile/img/日時情報/02/20/日時情報/52/02/8996319_4b86000332fa62d00000392d64a00000_170.jpg;
###instagram
InstagramはUUIDを組み合わせて管理しているようです。パスが長いですね。
アイコン:
https: //scontent-nrt1-1.cdninstagram.com/vp/26223423_148400000808676_7148163548756000000/5BC4422C/t51.2885-19/s150x150/26223423_148400000808676_7148163548756000000_n.jpg
写真:
https:// scontent-nrt1-1.cdninstagram.com/vp/26223423_148400000808676_7148163548756000000/t51.2885-15/s640x640/sh0.08/e35/31232925_800900000769909_8151073854442100000_n.jpg
###Zoho Docs(クラウドストレージ)
上記2サービスはURLをブラウザーに入力すると、画像をダウンロードできるのですが、
Zoho Docsでは表示できませんでした。アイコンは表示可能です。
当然というば当然ですが、プライベートな画像は表示する際に認証をしています。
写真:
https:// download.zoho.com/docs/prv/00000c661d00000f644738ec6a58018300000?_t=1526805269750
アイコン:
https:// accounts.zoho.com/file?fs=normal&nocache=10000805400000
#調査結果
ユーザーがアップロードした画像はUUIDや日にちなどをパスに使用して保存している。
画像共有サービスなど共有することが前提のサービスでは、
パスにUUIDなどを使用しているので予測しづらい。(そもそも公開情報なので予測されても問題ない)
プライベートで管理することが前提のサービスは、画像表示に何らかの認証を行なっている。
至極当たり前の結果となりましたが、一般的な画像の保管方法がわかりました。
#トビデモにおける実装
この情報を基礎に画像アップロード機能&表示部分を考えます。
トビデモはソフトウェアのデモを作成するツールです。
第三者に公開OKな画像をアップロードすることもあれば、BtoBでは特定の人だけの共有したい場面も予想されます。
現状はURLを知っていればだれでもデモにアクセスできますが、将来的にデモごとにアクセス制限を
つけることも想定していました。
ですので、一般には公開できない画像が含まれることを前提に考えました。
##画像保管
画像は以下のようなパスで、サーバーローカルの外部からアクセス不可な場所に保存します。
ユーザー登録していない場合、画像はtmpに保存され、30日間で削除されます。(削除機能は未実装)
ユーザー登録している場合は、ユーザーIDとデモIDを使用してディレクトリーを分けています。
画像は真似をして6桁のUUIDを生成して使用しました。
tmp/UUID.png ユーザーID/デモID/UUID.png
外部公開できるディレクトリに公開NG画像を保管するのは違和感があったので、
そもそもパスを知っていてもアクセスできない保管方法にしました。
画像のリクエストがあった際に、URLを返すのではなく、
画像をByte列に変換してそのままブラウザーに読み込んでもらうことで、
URLを渡すことなく、画像データを表示可能になりました。
画像のアクセスはこれで考えなくて良いので、あとは必要に応じて、
デモのリンクが呼ばれたときに認証を行う設定を入れれば、アクセス制御機能は実装できそうです。
うーん、正直これが正しかったのか疑問に思っています。
認証が必要な画像(デモ)だけ別ディレクトリーにして、そこは認証するとかでも良かったかもとか、
ブラウザーに直接Byte列渡した時の負荷って問題無いんだろうか、とかベストではなかったような気がしています。
(リンク渡してもどちらにせよデータダウンロードして読み込むわけだから変わらないはず?)
webサービスの画像処理周りの知見がまとまっている本とかあるのでしょうか。。
以下、画像を表示するときにByte列に変換して渡すところの実装です。
fileStream = new FileInputStream(user_folder + "/" + dropzoneList.get(i).getImg() + ".png");
ByteArrayOutputStream os = new ByteArrayOutputStream();
byte[] indata = new byte[10240 * 16];
int siz;
try {
while ((siz = fileStream.read(indata, 0, indata.length)) > 0) {
os.write(indata, 0, siz);
}
} catch (Exception e) {
// TODO 自動生成された catch ブロック
e.printStackTrace();
System.out.print("failed to read byte stream");
fileStream.close();
}
String base64 = new String(Base64.encodeBase64(os.toByteArray()), "ASCII");
// System.out.print(base64);
data = new StringBuffer();
data.append("data:image/png;base64,");
data.append(base64);
fileStream.close();
dropzoneWithImglist.add(new DropZoneWithImgBean(dropzoneList.get(i).getId(),
dropzoneList.get(i).getLinkid(), dropzoneList.get(i).getDropzone(),
dropzoneList.get(i).getImg(), data.toString(), width, height));
} else {
dropzoneWithImglist.add(new DropZoneWithImgBean(dropzoneList.get(i).getId(),
dropzoneList.get(i).getLinkid(), dropzoneList.get(i).getDropzone(),
dropzoneList.get(i).getImg(), "./../../img/default-none.png", 1300, 1000));
}
#まとめ
画像はローカルに保管ものだと思って、VPSを選ぶときもHDDに余裕があるサーバーを選んだのですが、
みなさん画像はS3に保管するんですね。今回は結果的にローカルに保管する必要があったのでよかったのですが今度から画像はS3に保管しよう。