メインコンテンツまでスキップ

Azure Storage アクセスガイド

Frontend/Backend アプリケーションから Azure Blob Storage と File Share にアクセスする方法を説明します。

概要

各環境(dev/prd)では、Frontend/Backend ごとに以下のストレージリソースが提供されます:

ストレージ構成: Storage Account → File Shares(Frontend/Backend ごとに /mnt/data にマウント)、Blob Containers(Frontend/Backend ごとに分離)

用途の違い:

  • File Share: コンテナに /mnt/data としてマウント可能、通常のファイル I/O、設定ファイル・一時ファイル・共有データに適している
  • Blob Storage: マウント不可・API アクセスのみ、Azure SDK、画像・動画・大容量ファイル・アーカイブに適している

環境変数

enable_storage = true の場合、以下の環境変数が自動的に設定されます:

共通環境変数: AZURE_STORAGE_ACCOUNT_NAME, AZURE_STORAGE_ACCOUNT_KEY, AZURE_STORAGE_CONNECTION_STRING, AZURE_STORAGE_BLOB_ENDPOINT, AZURE_STORAGE_CONTAINER_NAME

注意: AZURE_STORAGE_CONTAINER_NAME は Frontend/Backend で異なる値が設定されます。


File Share (ファイル共有) へのアクセス

マウントパス: /mnt/data(Frontend/Backend ともに同じパスですが、異なる File Shareがマウントされているため、データは分離されています)

アクセス方法: 通常のファイルシステム操作で読み書きできます。

Python (Backend) の例

import os

# ファイルの書き込み
with open('/mnt/data/config.json', 'w') as f:
f.write('{"key": "value"}')

# ファイルの読み込み
with open('/mnt/data/config.json', 'r') as f:
data = f.read()

# ディレクトリの作成
os.makedirs('/mnt/data/uploads', exist_ok=True)

# ファイルの存在チェック
if os.path.exists('/mnt/data/config.json'):
print("File exists")

# ファイル一覧の取得
files = os.listdir('/mnt/data')

Node.js (Frontend) の例

const fs = require("fs");
const path = require("path");

// ファイルの書き込み
fs.writeFileSync("/mnt/data/config.json", JSON.stringify({ key: "value" }));

// ファイルの読み込み
const data = fs.readFileSync("/mnt/data/config.json", "utf8");
const config = JSON.parse(data);

// ディレクトリの作成
fs.mkdirSync("/mnt/data/uploads", { recursive: true });

// ファイルの存在チェック
if (fs.existsSync("/mnt/data/config.json")) {
console.log("File exists");
}

// ファイル一覧の取得
const files = fs.readdirSync("/mnt/data");

ユースケース

  • ✅ アプリケーション設定ファイルの保存
  • ✅ セッションデータの永続化
  • ✅ 一時ファイルの保存(アップロード中のファイルなど)
  • ✅ ログファイルの保存
  • ✅ キャッシュファイルの保存

Blob Storage へのアクセス

認証情報の取得

環境変数から接続情報を取得します:

import os

connection_string = os.getenv("AZURE_STORAGE_CONNECTION_STRING")
container_name = os.getenv("AZURE_STORAGE_CONTAINER_NAME")

Python (Backend) でのアクセス

インストール

pip install azure-storage-blob

基本的な使用方法

import os
from azure.storage.blob import BlobServiceClient, BlobClient, ContainerClient

# 接続情報を環境変数から取得
connection_string = os.getenv("AZURE_STORAGE_CONNECTION_STRING")
container_name = os.getenv("AZURE_STORAGE_CONTAINER_NAME")

# BlobServiceClient の作成
blob_service_client = BlobServiceClient.from_connection_string(connection_string)
container_client = blob_service_client.get_container_client(container_name)

# ファイルのアップロード
def upload_file(file_path, blob_name):
"""ファイルをBlobにアップロード"""
blob_client = container_client.get_blob_client(blob_name)
with open(file_path, "rb") as data:
blob_client.upload_blob(data, overwrite=True)
print(f"Uploaded {blob_name}")

# ファイルのダウンロード
def download_file(blob_name, download_path):
"""Blobからファイルをダウンロード"""
blob_client = container_client.get_blob_client(blob_name)
with open(download_path, "wb") as download_file:
download_file.write(blob_client.download_blob().readall())
print(f"Downloaded {blob_name}")

# Blob一覧の取得
def list_blobs():
"""Containerに存在するBlobの一覧を取得"""
blob_list = container_client.list_blobs()
for blob in blob_list:
print(f"Name: {blob.name}, Size: {blob.size} bytes")

# Blobの削除
def delete_blob(blob_name):
"""Blobを削除"""
blob_client = container_client.get_blob_client(blob_name)
blob_client.delete_blob()
print(f"Deleted {blob_name}")

# Blobの存在チェック
def blob_exists(blob_name):
"""Blobが存在するかチェック"""
blob_client = container_client.get_blob_client(blob_name)
return blob_client.exists()

FastAPI での使用例

from fastapi import FastAPI, UploadFile, File
from azure.storage.blob import BlobServiceClient
import os

app = FastAPI()

connection_string = os.getenv("AZURE_STORAGE_CONNECTION_STRING")
container_name = os.getenv("AZURE_STORAGE_CONTAINER_NAME")
blob_service_client = BlobServiceClient.from_connection_string(connection_string)
container_client = blob_service_client.get_container_client(container_name)

@app.post("/upload")
async def upload_file(file: UploadFile = File(...)):
"""ファイルをBlobにアップロード"""
blob_client = container_client.get_blob_client(file.filename)
content = await file.read()
blob_client.upload_blob(content, overwrite=True)
return {"filename": file.filename, "size": len(content)}

@app.get("/files")
async def list_files():
"""Blob一覧を取得"""
blob_list = container_client.list_blobs()
files = [{"name": blob.name, "size": blob.size} for blob in blob_list]
return {"files": files}

@app.get("/download/{filename}")
async def download_file(filename: str):
"""Blobからファイルをダウンロード"""
blob_client = container_client.get_blob_client(filename)
if not blob_client.exists():
return {"error": "File not found"}

blob_data = blob_client.download_blob().readall()
return Response(content=blob_data, media_type="application/octet-stream")

Node.js (Frontend) でのアクセス

インストール

npm install @azure/storage-blob

基本的な使用方法

const { BlobServiceClient } = require("@azure/storage-blob");

// 接続情報を環境変数から取得
const connectionString = process.env.AZURE_STORAGE_CONNECTION_STRING;
const containerName = process.env.AZURE_STORAGE_CONTAINER_NAME;

// BlobServiceClient の作成
const blobServiceClient =
BlobServiceClient.fromConnectionString(connectionString);
const containerClient = blobServiceClient.getContainerClient(containerName);

// ファイルのアップロード
async function uploadFile(filePath, blobName) {
const blockBlobClient = containerClient.getBlockBlobClient(blobName);
const fs = require("fs");
const data = fs.readFileSync(filePath);
await blockBlobClient.upload(data, data.length);
console.log(`Uploaded ${blobName}`);
}

// ファイルのダウンロード
async function downloadFile(blobName, downloadPath) {
const blockBlobClient = containerClient.getBlockBlobClient(blobName);
const fs = require("fs");
const downloadBlockBlobResponse = await blockBlobClient.download(0);
const writeStream = fs.createWriteStream(downloadPath);
downloadBlockBlobResponse.readableStreamBody.pipe(writeStream);
console.log(`Downloaded ${blobName}`);
}

// Blob一覧の取得
async function listBlobs() {
const blobs = [];
for await (const blob of containerClient.listBlobsFlat()) {
blobs.push({ name: blob.name, size: blob.properties.contentLength });
}
return blobs;
}

// Blobの削除
async function deleteBlob(blobName) {
const blockBlobClient = containerClient.getBlockBlobClient(blobName);
await blockBlobClient.delete();
console.log(`Deleted ${blobName}`);
}

// Blobの存在チェック
async function blobExists(blobName) {
const blockBlobClient = containerClient.getBlockBlobClient(blobName);
return await blockBlobClient.exists();
}

Next.js API Routes での使用例

// pages/api/upload.js
import { BlobServiceClient } from "@azure/storage-blob";
import formidable from "formidable";
import fs from "fs";

export const config = {
api: {
bodyParser: false,
},
};

const connectionString = process.env.AZURE_STORAGE_CONNECTION_STRING;
const containerName = process.env.AZURE_STORAGE_CONTAINER_NAME;

export default async function handler(req, res) {
if (req.method !== "POST") {
return res.status(405).json({ error: "Method not allowed" });
}

const form = formidable({});
const [fields, files] = await form.parse(req);
const file = files.file[0];

const blobServiceClient =
BlobServiceClient.fromConnectionString(connectionString);
const containerClient = blobServiceClient.getContainerClient(containerName);
const blockBlobClient = containerClient.getBlockBlobClient(
file.originalFilename
);

const data = fs.readFileSync(file.filepath);
await blockBlobClient.upload(data, data.length);

res.status(200).json({
filename: file.originalFilename,
size: file.size,
});
}

ユースケース

  • ✅ ユーザーアップロードファイルの保存(画像、動画、PDF 等)
  • ✅ バックアップファイルの保存
  • ✅ レポートやエクスポートファイルの生成・保存
  • ✅ メディアファイルの CDN 配信
  • ✅ 大容量データのアーカイブ

使用例

シナリオ 1: ユーザーがアップロードした画像を保存

Backend (FastAPI):

from fastapi import FastAPI, UploadFile, File
from azure.storage.blob import BlobServiceClient
import os
from datetime import datetime

app = FastAPI()

connection_string = os.getenv("AZURE_STORAGE_CONNECTION_STRING")
container_name = os.getenv("AZURE_STORAGE_CONTAINER_NAME")
blob_service_client = BlobServiceClient.from_connection_string(connection_string)
container_client = blob_service_client.get_container_client(container_name)

@app.post("/upload-image")
async def upload_image(file: UploadFile = File(...)):
# ユニークなファイル名を生成
timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
blob_name = f"/img/{timestamp}_{file.filename}"

# Blobにアップロード
blob_client = container_client.get_blob_client(blob_name)
content = await file.read()
blob_client.upload_blob(content, overwrite=True)

# URLを生成(公開URLが必要な場合はSASトークンを使用)
blob_url = blob_client.url

return {
"filename": file.filename,
"blob_name": blob_name,
"url": blob_url,
"size": len(content)
}

シナリオ 2: 設定ファイルを File Share に保存

Backend (Python):

import json
import os

def save_user_config(user_id: str, config: dict):
"""ユーザー設定をFile Shareに保存"""
config_dir = f"/mnt/data/configs/{user_id}"
os.makedirs(config_dir, exist_ok=True)

config_file = f"{config_dir}/settings.json"
with open(config_file, 'w') as f:
json.dump(config, f, indent=2)

print(f"Saved config for user {user_id}")

def load_user_config(user_id: str) -> dict:
"""ユーザー設定をFile Shareから読み込み"""
config_file = f"/mnt/data/configs/{user_id}/settings.json"

if not os.path.exists(config_file):
return {}

with open(config_file, 'r') as f:
return json.load(f)

シナリオ 3: レポート生成と Blob 保存

Backend (Python):

import pandas as pd
from io import BytesIO
from azure.storage.blob import BlobServiceClient
import os

def generate_and_save_report(data: list, report_name: str):
"""データからレポートを生成してBlobに保存"""
# DataFrameを作成
df = pd.DataFrame(data)

# ExcelファイルをメモリにExcel生成
excel_buffer = BytesIO()
df.to_excel(excel_buffer, index=False, engine='openpyxl')
excel_buffer.seek(0)

# Blobにアップロード
connection_string = os.getenv("AZURE_STORAGE_CONNECTION_STRING")
container_name = os.getenv("AZURE_STORAGE_CONTAINER_NAME")
blob_service_client = BlobServiceClient.from_connection_string(connection_string)
container_client = blob_service_client.get_container_client(container_name)

blob_name = f"reports/{report_name}.xlsx"
blob_client = container_client.get_blob_client(blob_name)
blob_client.upload_blob(excel_buffer.getvalue(), overwrite=True)

return blob_client.url

トラブルシューティング

File Share にアクセスできない

症状: /mnt/data にファイルを書き込めない

原因と対処:

  1. enable_storage = true が設定されているか確認
  2. Terraform が正しく apply されているか確認
  3. Container App が再起動されているか確認(設定変更後)
# File Shareが正しくマウントされているか確認
ls -la /mnt/data

# 書き込み権限があるか確認
touch /mnt/data/test.txt

Blob Storage に接続できない

症状: AuthenticationErrorResourceNotFoundError

原因と対処:

  1. 環境変数が正しく設定されているか確認:
import os
print(os.getenv("AZURE_STORAGE_CONNECTION_STRING"))
print(os.getenv("AZURE_STORAGE_CONTAINER_NAME"))
  1. Container 名が正しいか確認(Frontend/Backend で異なる)

  2. Storage Account のアクセスキーが正しいか確認

パフォーマンスが遅い

File Share:

  • 大量の小さなファイルへのアクセスは遅い
  • 可能な限りファイルを結合するか、Blob を使用

Blob Storage:

  • 同時アクセス数に制限がある場合、CDN の利用を検討
  • 大容量ファイルは並列アップロード/ダウンロードを使用
# 並列アップロード例
from azure.storage.blob import BlobServiceClient

blob_client.upload_blob(
data,
overwrite=True,
max_concurrency=4 # 並列度を指定
)

まとめ

  • File Share (/mnt/data): 設定ファイル、一時ファイル、キャッシュに最適
  • Blob Storage: 大容量ファイル、メディアファイル、アーカイブに最適
  • Frontend/Backend で環境変数は自動設定される
  • Frontend/Backend で異なる Storage リソースが割り当てられる

適切なストレージを選択して、効率的なアプリケーションを構築してください!