はじめに
「ユーザーが画像をアップロードする機能を作りたいけど、サーバーの容量が心配...」
「ファイルのバックアップ、どうやって管理すればいいの?」
そんな悩みを解決してくれるのがAWS S3です。
今回は「S3って聞いたことあるけど、結局何なの?」という方向けに、S3の基本から実際の使い方まで、わかりやすく解説してみます!
AWS S3って何?
S3(Simple Storage Service)は、Amazon Web Servicesが提供するクラウドストレージサービスです。
身近な例で説明
GoogleドライブやDropboxのプログラマー版だと思ってください。
- 個人用クラウドストレージ: 写真や文書を保存
- S3: プログラムが使うファイルを保存
違いは何?
- 容量がほぼ無制限
- API経由でプログラムから操作できる
- 世界中どこからでも高速アクセス
- 堅牢性がハンパない(99.999999999%の耐久性)
なんでS3がいいの?
1. 容量を気にしなくていい
従来のサーバー:
サーバー容量: 100GB
「あー、もうすぐ容量いっぱい...HDDを増設しないと」
S3の場合:
使った分だけ課金
「容量?気にしたことないです」
2. バックアップが自動
従来:
「定期的にバックアップ取らないと...」
「バックアップHDDが壊れた...」
S3:
自動で複数の場所にコピー保存
「データが消える?まずありえないです」
3. CDN(高速配信)が使える
従来:
「海外のユーザーには画像表示が遅い...」
S3 + CloudFront:
「世界中どこでも高速表示!」
基本概念の説明
バケット = フォルダ
S3では、ファイルを「バケット」という入れ物に保存します。
my-app-bucket/ ← バケット名
├── images/
│ ├── user-avatars/
│ │ ├── user1.jpg
│ │ └── user2.png
│ └── product-photos/
│ ├── product1.jpg
│ └── product2.jpg
├── documents/
│ └── terms.pdf
└── backup/
└── database-backup.sql
キー = ファイルパス
各ファイルには「キー」という名前が付きます:
images/user-avatars/user1.jpg ← これがキー
普通のファイルパスと同じ感覚です。
実際に使ってみよう
1. バケットを作る
AWS Consoleで作成:
- AWS Consoleにログイン
- S3サービスを選択
- 「バケットを作成」をクリック
- バケット名を入力(全世界で一意でないとダメ)
- リージョンを選択(日本なら
ap-northeast-1
)
バケット名の例:
✅ good: my-app-images-2024
✅ good: company-backups-prod
❌ bad: images (すでに使われてる)
❌ bad: My App Images (大文字・スペースダメ)
2. Node.jsからS3を操作
// まずライブラリをインストール
// npm install @aws-sdk/client-s3
const { S3Client, PutObjectCommand, GetObjectCommand } = require('@aws-sdk/client-s3');
// S3クライアントを作成
const s3Client = new S3Client({
region: 'ap-northeast-1',
credentials: {
accessKeyId: 'YOUR_ACCESS_KEY',
secretAccessKey: 'YOUR_SECRET_KEY'
}
});
// ファイルをアップロード
async function uploadFile(bucketName, fileName, fileContent) {
try {
const command = new PutObjectCommand({
Bucket: bucketName,
Key: fileName,
Body: fileContent,
ContentType: 'image/jpeg' // ファイルの種類
});
const result = await s3Client.send(command);
console.log('アップロード成功:', result);
// 公開URLを生成
const url = `https://${bucketName}.s3.ap-northeast-1.amazonaws.com/${fileName}`;
return url;
} catch (error) {
console.error('アップロード失敗:', error);
}
}
// 使用例
uploadFile('my-app-images', 'users/avatar.jpg', imageBuffer);
3. Express.jsでファイルアップロード機能
const express = require('express');
const multer = require('multer'); // ファイルアップロード用
const { S3Client, PutObjectCommand } = require('@aws-sdk/client-s3');
const app = express();
const upload = multer(); // メモリに一時保存
const s3Client = new S3Client({ region: 'ap-northeast-1' });
// ファイルアップロードのエンドポイント
app.post('/upload', upload.single('image'), async (req, res) => {
try {
if (!req.file) {
return res.status(400).json({ error: 'ファイルが選択されていません' });
}
// S3にアップロード
const fileName = `images/${Date.now()}-${req.file.originalname}`;
const command = new PutObjectCommand({
Bucket: 'my-app-bucket',
Key: fileName,
Body: req.file.buffer,
ContentType: req.file.mimetype
});
await s3Client.send(command);
// 成功レスポンス
const fileUrl = `https://my-app-bucket.s3.amazonaws.com/${fileName}`;
res.json({
message: 'アップロード成功',
url: fileUrl
});
} catch (error) {
console.error('アップロードエラー:', error);
res.status(500).json({ error: 'アップロードに失敗しました' });
}
});
4. Reactでファイルアップロード
import React, { useState } from 'react';
function FileUpload() {
const [file, setFile] = useState(null);
const [uploading, setUploading] = useState(false);
const [uploadedUrl, setUploadedUrl] = useState('');
const handleFileSelect = (event) => {
setFile(event.target.files[0]);
};
const handleUpload = async () => {
if (!file) return;
setUploading(true);
const formData = new FormData();
formData.append('image', file);
try {
const response = await fetch('/upload', {
method: 'POST',
body: formData
});
const result = await response.json();
setUploadedUrl(result.url);
alert('アップロード成功!');
} catch (error) {
alert('アップロード失敗...');
} finally {
setUploading(false);
}
};
return (
<div>
<input
type="file"
accept="image/*"
onChange={handleFileSelect}
/>
<button
onClick={handleUpload}
disabled={!file || uploading}
>
{uploading ? 'アップロード中...' : 'アップロード'}
</button>
{uploadedUrl && (
<div>
<p>アップロード完了:</p>
<img src={uploadedUrl} alt="アップロード画像" style={{maxWidth: '300px'}} />
</div>
)}
</div>
);
}
S3の料金体系
基本料金(東京リージョン)
- ストレージ料金: 1GBあたり約3円/月
- リクエスト料金: 1000回あたり約0.5円
- データ転送料金: 1GBあたり約10円
実際の例:
100MB の画像ファイル × 1000枚 = 100GB
月額料金: 100GB × 3円 = 約300円
1日1000回のアクセス × 30日 = 30,000回
リクエスト料金: 30,000回 × 0.0005円 = 約15円
合計: 約315円/月
小さなアプリなら、月数百円程度で運用できます!
セキュリティ・権限設定
1. バケットポリシー
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": "*",
"Action": "s3:GetObject",
"Resource": "arn:aws:s3:::my-public-bucket/*"
}
]
}
意味: このバケットの中身は誰でも見ることができる(公開設定)
2. プライベートファイルの一時公開URL
const { getSignedUrl } = require('@aws-sdk/s3-request-presigner');
// 1時間だけ有効なダウンロードURL
async function generateDownloadUrl(bucketName, fileName) {
const command = new GetObjectCommand({
Bucket: bucketName,
Key: fileName
});
const url = await getSignedUrl(s3Client, command, { expiresIn: 3600 }); // 1時間
return url;
}
よくある使い方パターン
1. ユーザープロフィール画像
// アップロード時にファイル名をリネーム
const fileName = `profiles/${userId}-${Date.now()}.jpg`;
2. 静的サイトホスティング
// HTMLファイルをS3に置いて、Webサイトとして公開
// バケット設定で「Static website hosting」を有効にするだけ
3. ログファイルの保存
// アプリケーションログを日付別に保存
const logFileName = `logs/${new Date().toISOString().split('T')[0]}.log`;
つまずきやすいポイント
1. CORS(Cross-Origin)エラー
ブラウザから直接S3にアクセスする時:
[
{
"AllowedHeaders": ["*"],
"AllowedMethods": ["GET", "PUT", "POST"],
"AllowedOrigins": ["https://myapp.com"],
"ExposeHeaders": []
}
]
2. 権限エラー
「Access Denied」が出る時:
- IAMユーザーにS3の権限があるか確認
- バケットポリシーが正しいか確認
- リージョンが合ってるか確認
3. ファイル名の日本語
// 日本語ファイル名はエンコードが必要
const encodedFileName = encodeURIComponent('日本語ファイル名.jpg');
まとめ
AWS S3は、最初は「難しそう...」と思うかもしれませんが、基本的な使い方は意外と簡単です。
S3を使うメリット:
- サーバーの容量を気にしなくていい
- 自動バックアップで安心
- 世界中から高速アクセス
- 使った分だけ課金で経済的
こんな時に使おう:
- ユーザーのプロフィール画像
- 商品の写真
- PDFファイルのダウンロード機能
- ログファイルの保管
- 静的サイトのホスティング
最初は「ファイルアップロード機能」から始めて、慣れてきたらCDN(CloudFront)との組み合わせとかも試してみてください。
クラウドサービスって最初は敷居が高く感じますが、使い始めると「なんで今まで使わなかったんだろう」って思いますよ!☁️