【Next.js AWS】音声文字起こし&要約、分析アプリケーション(S3)(Transcribe)(Amazon Bedrock)
更新日:2025/02/02

基本機能
- 音声ファイルをアップロード
- アップロードした音声を文字に変換(Transcribe)
- 変換したテキストをプロンプトを指定してBedrockで処理
- 結果表示
AWS、Next.jsで実装の理由
AWS SDKの充実
- Node.js用のSDKが完備
Next.jsのAPI Routes
- フロントエンドとバックエンドを統合できる
- APIエンドポイントの作成が簡単
AWSサービス間の連携
- S3、Transcribe、Bedrockが統一的な認証情報で利用可能
手順
Next.jsプロジェクトの作成
mkdir next-app; cd next-app; npx create-next-app@latest . --typescript --tailwind --app --src-dir --yes
フロントページ next-app\src\app\page.tsx
"use client";
import { useState, FormEvent } from "react";
export default function Home() {
const [file, setFile] = useState<File | null>(null);
const [prompt, setPrompt] = useState("");
const [isLoading, setIsLoading] = useState(false);
const [jobName, setJobName] = useState<string | null>(null);
const [transcribeStatus, setTranscribeStatus] = useState<string | null>(null);
const [transcribeResult, setTranscribeResult] = useState<string | null>(null);
const [transcribeText, setTranscribeText] = useState<string | null>(null);
const [bedrockResult, setBedrockResult] = useState<string | null>(null);
// transcribeTextが設定された時にBedrockを呼び出す
const processWithBedrock = async (text: string) => {
try {
const response = await fetch("/api/bedrock", {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({ text, prompt }),
});
const data = await response.json();
if (data.result) {
setBedrockResult(data.result);
}
} catch (error) {
console.error("Bedrock処理エラー:", error);
}
};
const handleSubmit = async (e: FormEvent) => {
e.preventDefault();
if (!file) return;
setIsLoading(true);
try {
const formData = new FormData();
formData.append("file", file);
const response = await fetch("/api/upload", {
method: "POST",
body: formData,
});
const data = await response.json();
if (!response.ok) throw new Error(data.error);
if (data.jobName) {
setJobName(data.jobName);
checkTranscribeStatus(data.jobName);
}
} catch (error) {
console.error("エラー:", error);
} finally {
setIsLoading(false);
}
};
const checkTranscribeStatus = async (jobName: string) => {
try {
const response = await fetch(`/api/transcribe/status?jobName=${jobName}`);
const data = await response.json();
setTranscribeStatus(data.status);
if (data.status !== "COMPLETED") {
setTimeout(() => checkTranscribeStatus(jobName), 5000);
} else if (data.transcript) {
setTranscribeText(data.transcript);
// Bedrockでテキスト処理
await processWithBedrock(data.transcript);
}
} catch (error) {
console.error("ステータス確認エラー:", error);
}
};
return (
<div className="p-4 max-w-2xl mx-auto">
<h1 className="text-xl mb-4">音声文字起こし分析</h1>
<form onSubmit={handleSubmit} className="space-y-4">
<div>
<label className="block mb-2">音声ファイルを選択</label>
<input
type="file"
accept="audio/*"
onChange={(e) => setFile(e.target.files?.[0] || null)}
className="w-full p-2 border"
required
/>
{file && <div className="mt-2 text-sm">選択中: {file.name}</div>}
</div>
<div>
<label className="block mb-2">プロンプト</label>
<textarea
value={prompt}
onChange={(e) => setPrompt(e.target.value)}
className="w-full p-2 border"
rows={3}
placeholder="例:要約してください、分析してください、重要なポイントを箇条書きにしてください" // プレースホルダーを具体的に
required
/>
</div>
<button
type="submit"
disabled={isLoading}
className="px-4 py-2 bg-gray-200 hover:bg-gray-300 disabled:bg-gray-100"
>
{isLoading ? "処理中..." : "処理開始"}
</button>
</form>
{/* 処理状態の表示 */}
{transcribeStatus && (
<div className="mt-4 p-4 bg-gray-50 rounded">
<h2 className="font-bold mb-2">処理状態</h2>
<p>{transcribeStatus}</p>
</div>
)}
{/* 処理結果の表示 */}
<div className="space-y-4">
{transcribeText && (
<div className="mt-4 p-4 bg-gray-50 rounded">
<h2 className="font-bold mb-2">文字起こし結果</h2>
<p className="whitespace-pre-wrap">{transcribeText}</p>
</div>
)}
{bedrockResult && (
<div className="mt-4 p-4 bg-gray-50 rounded border-t-4 border-blue-500">
<h2 className="font-bold mb-2">AIによる分析結果</h2>{" "}
{/* 「要約結果」から変更 */}
<p className="whitespace-pre-wrap">{bedrockResult}</p>
</div>
)}
</div>
</div>
);
}
AWS SDK のセットアップと S3 へのアップロード機能を実装
AWS SDK のインストール
npm install @aws-sdk/client-s3
既存ユーザーにAWS管理ポリシーを直接アタッチ
- IAMコンソール → ユーザー → 該当ユーザー
- 「許可を追加」→ 「既存のポリシーを直接アタッチ」
- 以下の管理ポリシーを選択:
AmazonS3FullAccess
AmazonTranscribeFullAccess
BedrockFullAccess
バケットの作成
next-app\src\app\api\upload\route.ts
import { NextResponse } from 'next/server'
import { S3Client, PutObjectCommand } from '@aws-sdk/client-s3'
import { TranscribeClient, StartTranscriptionJobCommand } from '@aws-sdk/client-transcribe'
// S3クライアントの初期化
const s3Client = new S3Client({
region: process.env.NEXT_PUBLIC_AWS_REGION || 'ap-northeast-1',
credentials: {
accessKeyId: process.env.AWS_ACCESS_KEY_ID!,
secretAccessKey: process.env.AWS_SECRET_ACCESS_KEY!,
}
})
// Transcribeクライアントの初期化
const transcribeClient = new TranscribeClient({
region: process.env.NEXT_PUBLIC_AWS_REGION || 'ap-northeast-1',
credentials: {
accessKeyId: process.env.AWS_ACCESS_KEY_ID!,
secretAccessKey: process.env.AWS_SECRET_ACCESS_KEY!,
}
})
export async function POST(request: Request) {
try {
const formData = await request.formData()
const file = formData.get('file') as File
if (!file) {
return NextResponse.json({ error: 'No file provided' }, { status: 400 })
}
// ファイルをArrayBufferに変換
const buffer = Buffer.from(await file.arrayBuffer())
// ファイル名に現在時刻を追加して一意にする
const fileKey = `uploads/${Date.now()}-${file.name}`
// S3にアップロード
await s3Client.send(
new PutObjectCommand({
Bucket: process.env.NEXT_PUBLIC_S3_BUCKET_NAME,
Key: fileKey,
Body: buffer,
ContentType: file.type,
})
)
// Transcribeジョブを開始
const jobName = `transcribe-${Date.now()}`
await transcribeClient.send(
new StartTranscriptionJobCommand({
TranscriptionJobName: jobName,
LanguageCode: 'ja-JP',
Media: {
MediaFileUri: `s3://${process.env.NEXT_PUBLIC_S3_BUCKET_NAME}/${fileKey}`
},
OutputBucketName: process.env.NEXT_PUBLIC_S3_BUCKET_NAME
})
)
return NextResponse.json({
success: true,
fileKey,
jobName,
bucket: process.env.NEXT_PUBLIC_S3_BUCKET_NAME
})
} catch (error) {
console.error('Upload error:', error)
return NextResponse.json({ error: 'Upload failed' }, { status: 500 })
}
}
next-app/.env.example
# AWS Credentials
AWS_ACCESS_KEY_ID=あなたのアクセスキーID
AWS_SECRET_ACCESS_KEY=あなたのシークレットアクセスキー
NEXT_PUBLIC_AWS_REGION=S3バケットにリージョン
NEXT_PUBLIC_S3_BUCKET_NAME=あなたのバケット名
Transcribeの設定
# Transcribe用のSDKをインストール
npm install @aws-sdk/client-transcribe
↓音声ファイル素材
next-app\src\app\api\transcribe\status\route.ts
import { NextResponse } from 'next/server'
import { TranscribeClient, GetTranscriptionJobCommand } from '@aws-sdk/client-transcribe'
import { S3Client, GetObjectCommand } from '@aws-sdk/client-s3'
const transcribeClient = new TranscribeClient({
region: process.env.NEXT_PUBLIC_AWS_REGION || 'ap-northeast-1',
credentials: {
accessKeyId: process.env.AWS_ACCESS_KEY_ID!,
secretAccessKey: process.env.AWS_SECRET_ACCESS_KEY!,
}
})
const s3Client = new S3Client({
region: process.env.NEXT_PUBLIC_AWS_REGION || 'ap-northeast-1',
credentials: {
accessKeyId: process.env.AWS_ACCESS_KEY_ID!,
secretAccessKey: process.env.AWS_SECRET_ACCESS_KEY!,
}
})
export async function GET(request: Request) {
try {
const { searchParams } = new URL(request.url)
const jobName = searchParams.get('jobName')
if (!jobName) {
return NextResponse.json({ error: 'Job name is required' }, { status: 400 })
}
const command = new GetTranscriptionJobCommand({
TranscriptionJobName: jobName
})
const response = await transcribeClient.send(command)
const job = response.TranscriptionJob
// デバッグログを追加
console.log('Transcribe Job Status:', job?.TranscriptionJobStatus)
console.log('Transcribe Job:', job)
// ジョブが完了していない場合は状態のみ返す
if (job?.TranscriptionJobStatus !== 'COMPLETED') {
return NextResponse.json({
status: job?.TranscriptionJobStatus
})
}
// 完了している場合は文字起こし結果を取得
try {
console.log('TranscriptFileUri:', job.Transcript?.TranscriptFileUri)
const transcriptPath = job.Transcript?.TranscriptFileUri
?.replace(`https://s3.${process.env.NEXT_PUBLIC_AWS_REGION}.amazonaws.com/${process.env.NEXT_PUBLIC_S3_BUCKET_NAME}/`, '');
console.log('Transcript Path:', transcriptPath)
if (transcriptPath) {
const s3Response = await s3Client.send(
new GetObjectCommand({
Bucket: process.env.NEXT_PUBLIC_S3_BUCKET_NAME!,
Key: transcriptPath
})
);
const transcriptJson = await s3Response.Body?.transformToString();
console.log('Transcript JSON:', transcriptJson)
const transcriptData = JSON.parse(transcriptJson || '{}');
return NextResponse.json({
status: 'COMPLETED',
transcript: transcriptData.results.transcripts[0].transcript
});
}
} catch (error) {
console.error('Error fetching transcript:', error);
}
return NextResponse.json({
status: 'COMPLETED',
error: 'Could not fetch transcript'
});
} catch (error) {
console.error('Status check error:', error)
return NextResponse.json({ error: 'Status check failed' }, { status: 500 })
}
}
Bedrockの処理
npm install @aws-sdk/client-bedrock-runtime
next-app\src\app\api\bedrock\route.ts
import { NextResponse } from 'next/server'
import { BedrockRuntimeClient, InvokeModelCommand } from '@aws-sdk/client-bedrock-runtime'
const bedrockClient = new BedrockRuntimeClient({
region: process.env.NEXT_PUBLIC_AWS_REGION || 'ap-northeast-1',
credentials: {
accessKeyId: process.env.AWS_ACCESS_KEY_ID!,
secretAccessKey: process.env.AWS_SECRET_ACCESS_KEY!,
}
})
export async function POST(request: Request) {
try {
const { text, prompt } = await request.json()
const response = await bedrockClient.send(
new InvokeModelCommand({
modelId: 'amazon.titan-text-express-v1',
contentType: 'application/json',
accept: 'application/json',
body: JSON.stringify({
inputText: `以下のテキストを${prompt}\n\n${text}`,
textGenerationConfig: {
maxTokenCount: 1000,
temperature: 0.7,
topP: 0.9,
stopSequences: []
}
})
})
)
const responseBody = JSON.parse(new TextDecoder().decode(response.body))
console.log('Bedrock response:', responseBody) // デバッグ用ログ
return NextResponse.json({ result: responseBody.results[0].outputText })
} catch (error) {
console.error('Bedrock error:', error)
return NextResponse.json({ error: 'Bedrock processing failed' }, { status: 500 })
}
}
実際のリクエストでは以下のように組み立てられています:
`以下のテキストを${prompt}\n\n${text}`
- “以下のテキストを” (固定文)
- “要約してください” (ユーザーが入力したプロンプト)
- 改行2回
- (文字起こしされたテキスト)
インフラ担当者への権限付与の依頼
【目的】
音声ファイルのアップロードから文字起こし、AI分析までを行うアプリケーションの開発
【必要な権限】
1. AmazonS3FullAccess
- 用途:音声ファイルの保存、文字起こし結果の取得
2. AmazonTranscribeFullAccess
- 用途:音声の文字起こし処理
3. BedrockFullAccess
- 用途:文字起こしされたテキストのAI分析
【対象ユーザー/ロール】
- ユーザー名:[開発用IAMユーザー名]

-
検索
(例) debug search etc.. -
カテゴリー
-
Windows↔️Macで文字化けしたZIPファイルを解決する方法【7-Zip活用ガイド】
更新日:2025/02/18
-
本番環境 / ステージング環境 でのGitブランチ切り替え方法と注意点 Note: witching to ‘origin/new_feature’. You are in ‘detached HEAD’ state…
更新日:2025/02/16
-
複数のWordPressサイトをまとめて検索・管理するには?
更新日:2025/02/16
-
【WordPress】SVGを投稿記事のサムネイルに!自作プラグイン(未実装🤷♂️)「nonce(ナンス)トークン」wp_postmetaテーブルを利用
更新日:2025/02/14
-
【WordPress】カスタムフィールド作成の仕方「ACF」Advanced Custom Fieldsの使用法
更新日:2024/06/10
298 view
-
mixhostでのWordPress利用について
更新日:2024/04/02
295 view
-
scrollHint
更新日:2024/03/13
246 view
-
LOLIPOPでWordPressインストール(サブディレクトリにインストールしたWordPressをドメイン直下で表示)
更新日:2024/06/13
235 view
-
XML Sitemap Generator for Google不具合のダウングレード対応、代替プラグイン
更新日:2024/06/13
234 view
-
Sass導入方法
更新日:2024/03/13
206 view
-
【SnowMonkey】納品までのフローまとめ
更新日:2025/02/15
163 view
-
【Snow Monkey】Googleサーチコンソール登録
更新日:2024/05/28
157 view
-
ワードプレスでフォントを変えたいとき、プラグインを使えば初心者でも簡単
更新日:2025/02/16
144 view
-
【Googleサーチコンソール】検索画面で表示させたくない「Googleのインデックス削除」
更新日:2024/06/07
129 view
-
【ショートコード】編集画面で任意の場所にHTMLファイルを挿入
更新日:2024/01/26
122 view
-
tailwindcss使い方
更新日:2024/03/13
119 view
-
キーワード検索で上位表示させる方法【SEO】
更新日:2023/10/28
119 view
-
Recline: GitHub Copilotで動作するClaude 3.5 Sonnet搭載の開発支援ツール
更新日:2025/01/20
105 view
-
WordPressプラグイン「All-in-One WP Migration」のトラブル
更新日:2022/03/31
83 view
-
【2022年】WordPressセキュリティー対策おすすめプラグイン2選
更新日:2022/02/21
70 view
-
【必須】ワードプレスセキュリティー対策まとめ【2022年】
更新日:2025/02/16
69 view
-
「THE THOR」を半年使ってみて、レビュー【メリット・デメリット】
更新日:2025/02/16
45 view
-
SSHを使用してGitHubのリポジトリをクローン、複数の接続元で公開鍵をディレクトリで区別する方法
更新日:2025/01/13
43 view
-
VSCodeで開発効率を劇的に向上させる!Roo-Clineプラグイン完全ガイド【Gemini API】
更新日:2025/02/08
41 view
-
「JSONスキーマ」と「Few-shot Learning」で実現する次世代生成AIソフトウェア開発の可能性
更新日:2025/02/12
40 view
-
THE THOR(ザ・トール)でグーグルアドセンスを使う
更新日:2025/02/16
39 view
-
WordPressの無料画像素材のおすすめダウンロードサイト!
更新日:2022/02/22
35 view
-
リファクタリング【VSCode Javasctipt Python】
更新日:2025/02/06
34 view
-
形態素解析とは?初心者でもわかるツールの使い方と実践例
更新日:2025/02/08
33 view
-
【Next.js AWS】音声文字起こし&要約、分析アプリケーション(S3)(Transcribe)(Amazon Bedrock)
更新日:2025/02/13
28 view
-
VSCodeプラグイン「 Pretter – Code formatter」「PHP Intelephense 」
更新日:2025/01/06
26 view
-
【CSS JavaScript サンプルコード】アコーディオン 続きを読む(Read more)Toggle 折りたたみコンテンツ
更新日:2025/02/05
23 view
-
【WordPress】SVGを投稿記事のサムネイルに!自作プラグイン(未実装🤷♂️)「nonce(ナンス)トークン」wp_postmetaテーブルを利用
更新日:2025/02/16
22 view
-
【WordPressプラグイン】人気記事 管理 WordPress Popular Posts 実践的なカスタマイズ
更新日:2025/02/15
22 view
-
Googleアドセンスとは?〜前編〜
更新日:2025/02/16
22 view
-
【WordPress】プラグイン不使用でカスタムタクソノミーを設定、絞り込み検索について
更新日:2025/01/26
18 view
-
スマートフォンファーストのWeb開発実践ガイド
更新日:2025/01/23
16 view
-
WordPressでカスタムフィールドを使った記事のスコアリングシステムの実装
更新日:2025/01/27
15 view
-
AWSのOCRサービス完全ガイド:Textract、Rekognition、Comprehendの使い分け
更新日:2025/02/09
14 view