feat:AI 음성녹음 GCS 파일 다운로드 엔드포인트 추가
- GoogleCloudService에 downloadFromStorage 메서드 추가 (GCS REST API 사용)
- AiVoiceRecordingController에 download 메서드 추가 (스트림 응답)
- 다운로드 라우트 추가 (GET /{id}/download)
- 파일명은 제목 기반으로 생성, Content-Disposition 헤더 설정
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -6,8 +6,10 @@
|
||||
use App\Models\AiVoiceRecording;
|
||||
use App\Models\Interview\InterviewCategory;
|
||||
use App\Services\AiVoiceRecordingService;
|
||||
use App\Services\GoogleCloudService;
|
||||
use Illuminate\Http\JsonResponse;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Str;
|
||||
use Illuminate\View\View;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
|
||||
@@ -223,4 +225,39 @@ public function status(int $id): JsonResponse
|
||||
],
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* GCS 음성파일 다운로드
|
||||
*/
|
||||
public function download(int $id): Response|JsonResponse
|
||||
{
|
||||
$recording = AiVoiceRecording::find($id);
|
||||
|
||||
if (! $recording || ! $recording->audio_file_path) {
|
||||
return response()->json([
|
||||
'success' => false,
|
||||
'message' => '파일을 찾을 수 없습니다.',
|
||||
], 404);
|
||||
}
|
||||
|
||||
$googleCloudService = app(GoogleCloudService::class);
|
||||
$content = $googleCloudService->downloadFromStorage($recording->audio_file_path);
|
||||
|
||||
if (! $content) {
|
||||
return response()->json([
|
||||
'success' => false,
|
||||
'message' => '파일 다운로드에 실패했습니다.',
|
||||
], 500);
|
||||
}
|
||||
|
||||
// 파일 확장자 추출
|
||||
$extension = pathinfo($recording->audio_file_path, PATHINFO_EXTENSION) ?: 'webm';
|
||||
|
||||
// 파일명 생성 (제목 기반, URL 안전하게)
|
||||
$filename = Str::slug($recording->title ?: 'recording').'.'. $extension;
|
||||
|
||||
return response($content)
|
||||
->header('Content-Type', 'audio/'.$extension)
|
||||
->header('Content-Disposition', 'attachment; filename="'.$filename.'"');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -317,6 +317,53 @@ public function deleteFromStorage(string $objectName): bool
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* GCS에서 파일 다운로드 (스트림)
|
||||
*/
|
||||
public function downloadFromStorage(string $objectName): ?string
|
||||
{
|
||||
$token = $this->getAccessToken();
|
||||
if (! $token) {
|
||||
Log::error('Google Cloud: 다운로드 토큰 획득 실패');
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
$bucket = config('services.google.storage_bucket');
|
||||
if (! $bucket) {
|
||||
Log::error('Google Cloud: Storage 버킷 설정 없음');
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
try {
|
||||
$url = 'https://storage.googleapis.com/storage/v1/b/'.
|
||||
urlencode($bucket).'/o/'.urlencode($objectName).'?alt=media';
|
||||
|
||||
$response = Http::withToken($token)->get($url);
|
||||
|
||||
if ($response->successful()) {
|
||||
Log::info('Google Cloud: Storage 다운로드 성공', [
|
||||
'object' => $objectName,
|
||||
'size' => strlen($response->body()),
|
||||
]);
|
||||
|
||||
return $response->body();
|
||||
}
|
||||
|
||||
Log::error('Google Cloud: Storage 다운로드 실패', [
|
||||
'status' => $response->status(),
|
||||
'response' => $response->body(),
|
||||
]);
|
||||
|
||||
return null;
|
||||
} catch (\Exception $e) {
|
||||
Log::error('Google Cloud: Storage 다운로드 예외', ['error' => $e->getMessage()]);
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 서비스 사용 가능 여부
|
||||
*/
|
||||
|
||||
@@ -423,6 +423,7 @@
|
||||
Route::post('/{id}/process', [AiVoiceRecordingController::class, 'processAudio'])->name('process');
|
||||
Route::delete('/{id}', [AiVoiceRecordingController::class, 'destroy'])->name('destroy');
|
||||
Route::get('/{id}/status', [AiVoiceRecordingController::class, 'status'])->name('status');
|
||||
Route::get('/{id}/download', [AiVoiceRecordingController::class, 'download'])->name('download');
|
||||
});
|
||||
|
||||
// 명함 OCR API
|
||||
|
||||
Reference in New Issue
Block a user