🐈
TypeScriptでGoogle Cloud Storageに音声ファイルをアップロードする方法
Google Cloud Storage (GCS) を利用して音声ファイルをアップロードする方法を紹介します。本記事では以下の2つの方法を解説します。
- GCS API を使用して直接アップロード
- Signed URL を使用してアップロード
それぞれの方法のコード例や使用シナリオを解説します。
1. GCS API を使用した直接アップロード
必要な準備
- Google Cloud プロジェクトの作成
-
バケットの作成
- GCS コンソール (https://console.cloud.google.com/storage) でバケットを作成します。
-
サービスアカウントの作成と認証情報の取得
- IAMと管理 > サービスアカウントからサービスアカウントを作成します。
- JSON形式で鍵をダウンロードし、プロジェクト内に配置します(安全に管理すること)。
TypeScript のコード
import { Storage } from '@google-cloud/storage';
import mime from 'mime-types';
import * as path from 'path';
import * as dotenv from 'dotenv';
import * as fs from 'fs';
dotenv.config();
const CONFIG = {
bucketName: process.env.BUCKET_NAME,
projectId: process.env.PROJECT_ID,
serviceAccountPath: path.join(__dirname, 'service-account-key.json'),
} as const;
class AudioUploadError extends Error {
constructor(message: string) {
super(message);
this.name = 'AudioUploadError';
}
}
const storage = new Storage({
projectId: CONFIG.projectId,
keyFilename: CONFIG.serviceAccountPath,
});
function validateAudioMimeType(filePath: string): string {
const mimeType = mime.lookup(filePath);
if (!mimeType || !mimeType.startsWith('audio/')) {
throw new AudioUploadError(`Invalid or non-audio file: ${filePath}`);
}
return mimeType;
}
async function uploadAudioFile(localFilePath: string, destinationFileName: string) {
if (!fs.existsSync(localFilePath)) {
throw new AudioUploadError(`File not found: ${localFilePath}`);
}
if (!CONFIG.bucketName) {
throw new AudioUploadError('BUCKET_NAME is not defined in environment');
}
const contentType = validateAudioMimeType(localFilePath);
const bucket = storage.bucket(CONFIG.bucketName);
await bucket.upload(localFilePath, {
destination: destinationFileName,
metadata: { contentType },
});
console.log(`File uploaded successfully: gs://${CONFIG.bucketName}/${destinationFileName}`);
}
async function main() {
try {
const audioFile = path.join(__dirname, 'sample.wav');
await uploadAudioFile(audioFile, 'sample-direct-upload.wav');
} catch (error) {
console.error('Upload failed:', error.message);
process.exit(1);
}
}
main();
環境変数の設定
.env ファイルに以下を記載します。
BUCKET_NAME=your-bucket-name
PROJECT_ID=your-project-id
2. Signed URL を使用したアップロード
Signed URL を使う理由
Signed URL を使用すると、アップロード処理を一時的に委譲できます。例えば、サーバーで認証情報を保持しないクライアントがファイルを直接アップロードしたい場合などに便利です。
以下のコードは Signed URL を生成し、それを使ってファイルをアップロードする例です。
import { Storage } from '@google-cloud/storage';
import fetch from 'node-fetch';
import mime from 'mime-types';
import * as path from 'path';
import * as dotenv from 'dotenv';
import * as fs from 'fs';
dotenv.config();
const CONFIG = {
bucketName: process.env.BUCKET_NAME,
projectId: process.env.PROJECT_ID,
serviceAccountPath: path.join(__dirname, 'service-account-key.json'),
} as const;
class AudioUploadError extends Error {
constructor(message: string) {
super(message);
this.name = 'AudioUploadError';
}
}
const storage = new Storage({
projectId: CONFIG.projectId,
keyFilename: CONFIG.serviceAccountPath,
});
function validateAudioMimeType(filePath: string): string {
const mimeType = mime.lookup(filePath);
if (!mimeType || !mimeType.startsWith('audio/')) {
throw new AudioUploadError(`Invalid or non-audio file: ${filePath}`);
}
return mimeType;
}
async function uploadAudioFilePresigned(localFilePath: string, destinationFileName: string) {
if (!fs.existsSync(localFilePath)) {
throw new AudioUploadError(`File not found: ${localFilePath}`);
}
if (!CONFIG.bucketName) {
throw new AudioUploadError('BUCKET_NAME is not defined in environment');
}
const mimeType = validateAudioMimeType(localFilePath);
const fileRef = storage.bucket(CONFIG.bucketName).file(destinationFileName);
const [signedUrl] = await fileRef.getSignedUrl({
version: 'v4',
action: 'write',
expires: Date.now() + 15 * 60 * 1000,
contentType: mimeType,
});
const fileBuffer = fs.readFileSync(localFilePath);
const response = await fetch(signedUrl, {
method: 'PUT',
headers: { 'Content-Type': mimeType },
body: fileBuffer,
});
if (!response.ok) {
throw new Error(`Failed to upload file to ${signedUrl}`);
}
console.log(`File uploaded via presigned URL: gs://${CONFIG.bucketName}/${destinationFileName}`);
}
async function main() {
try {
const audioFile = path.join(__dirname, 'sample.wav');
await uploadAudioFilePresigned(audioFile, 'sample-presigned.wav');
} catch (error) {
console.error('Upload failed:', error.message);
process.exit(1);
}
}
main();
結果の確認
アップロードが成功すると、Cloud Storage コンソールでアップロードされたファイルが確認できます。
注意点
-
サービスアカウントキーの管理: JSONキーは
.gitignoreに追加し、リポジトリに含めないようにしてください。 - 運用環境でのデフォルト認証情報の利用: 本番環境では gcloud auth を利用し、JSONキーを使用しない方法が推奨されます。
Discussion