diff --git a/rog/utils/s3_image_uploader.py b/rog/utils/s3_image_uploader.py index b28613f..4a7e3b5 100644 --- a/rog/utils/s3_image_uploader.py +++ b/rog/utils/s3_image_uploader.py @@ -19,13 +19,23 @@ class S3ImageUploader: def __init__(self): """S3クライアントを初期化""" try: + # AWS認証情報の確認 + aws_access_key = getattr(settings, 'AWS_ACCESS_KEY_ID', None) + aws_secret_key = getattr(settings, 'AWS_SECRET_ACCESS_KEY', None) + + if not aws_access_key or not aws_secret_key: + logger.warning("AWS credentials not configured, S3 upload will be disabled") + self.s3_client = None + self.bucket_name = None + return + self.s3_client = boto3.client( 's3', - aws_access_key_id=getattr(settings, 'AWS_ACCESS_KEY', None), - aws_secret_access_key=getattr(settings, 'AWS_SECRET_ACCESS_KEY', None), - region_name=getattr(settings, 'AWS_REGION', 'us-west-2') + aws_access_key_id=aws_access_key, + aws_secret_access_key=aws_secret_key, + region_name=getattr(settings, 'AWS_S3_REGION_NAME', 'us-west-2') ) - self.bucket_name = getattr(settings, 'S3_BUCKET_NAME', 'sumasenrogaining') + self.bucket_name = getattr(settings, 'AWS_STORAGE_BUCKET_NAME', 'sumasenrogaining') logger.info(f"S3 client initialized for bucket: {self.bucket_name}") except Exception as e: logger.error(f"Failed to initialize S3 client: {e}") diff --git a/rog/views_apis/api_bulk_photo_upload.py b/rog/views_apis/api_bulk_photo_upload.py index 28d345d..e91d518 100644 --- a/rog/views_apis/api_bulk_photo_upload.py +++ b/rog/views_apis/api_bulk_photo_upload.py @@ -68,14 +68,27 @@ def upload_photo_to_s3(uploaded_file, event_code, zekken_number, cp_number=None, temp_file_path = temp_file.name try: + # AWS認証情報の確認 + aws_access_key = getattr(settings, 'AWS_ACCESS_KEY_ID', None) + aws_secret_key = getattr(settings, 'AWS_SECRET_ACCESS_KEY', None) + + if not aws_access_key or not aws_secret_key: + logger.warning(f"[S3_UPLOAD] ❌ AWS credentials not configured, falling back to local storage") + return { + 'success': False, + 's3_url': None, + 's3_key': None, + 'error': 'AWS credentials not configured, using local storage instead' + } + # S3クライアントの設定(環境変数から取得) import boto3 - from botocore.exceptions import ClientError + from botocore.exceptions import ClientError, NoCredentialsError, PartialCredentialsError s3_client = boto3.client( 's3', - aws_access_key_id=getattr(settings, 'AWS_ACCESS_KEY_ID', None), - aws_secret_access_key=getattr(settings, 'AWS_SECRET_ACCESS_KEY', None), + aws_access_key_id=aws_access_key, + aws_secret_access_key=aws_secret_key, region_name=getattr(settings, 'AWS_S3_REGION_NAME', 'ap-northeast-1') ) @@ -122,13 +135,23 @@ def upload_photo_to_s3(uploaded_file, event_code, zekken_number, cp_number=None, 'error': 'S3 upload not available (boto3 not installed)' } except Exception as e: - logger.error(f"[S3_UPLOAD] ❌ Error uploading to S3: {str(e)}") - return { - 'success': False, - 's3_url': None, - 's3_key': None, - 'error': str(e) - } + error_message = str(e) + if 'credentials' in error_message.lower(): + logger.warning(f"[S3_UPLOAD] ❌ AWS credentials error: {error_message}") + return { + 'success': False, + 's3_url': None, + 's3_key': None, + 'error': f'AWS credentials error: {error_message}' + } + else: + logger.error(f"[S3_UPLOAD] ❌ Error uploading to S3: {error_message}") + return { + 'success': False, + 's3_url': None, + 's3_key': None, + 'error': str(e) + } def create_checkin_image_record(gps_checkin, s3_url, s3_key, original_filename, exif_data, request_id, user): """ @@ -147,17 +170,23 @@ def create_checkin_image_record(gps_checkin, s3_url, s3_key, original_filename, CheckinImagesオブジェクトまたはNone """ try: + # S3 URLがない場合はローカルパスまたは一時的なプレースホルダーを使用 + image_url = s3_url if s3_url else f"local://bulk_upload/{original_filename}" + # CheckinImagesレコードを作成 checkin_image = CheckinImages.objects.create( user=user, - checkinimage=s3_url, # S3のURLを画像URLとして保存 + checkinimage=image_url, # S3のURLまたはローカルパスを保存 checkintime=timezone.now(), team_name=f"Team_{gps_checkin.zekken}", # ゼッケン番号からチーム名を生成 event_code=gps_checkin.event_code, cp_number=gps_checkin.cp_number ) - logger.info(f"[CHECKIN_IMAGE] ✅ Created CheckinImages record - ID: {checkin_image.id}, checkin_id: {gps_checkin.id}") + if s3_url: + logger.info(f"[CHECKIN_IMAGE] ✅ Created CheckinImages record with S3 URL - ID: {checkin_image.id}, checkin_id: {gps_checkin.id}") + else: + logger.info(f"[CHECKIN_IMAGE] ✅ Created CheckinImages record with local path - ID: {checkin_image.id}, checkin_id: {gps_checkin.id}") return checkin_image @@ -384,17 +413,21 @@ def create_checkin_from_photo(entry, checkpoint, photo_datetime, zekken_number, ) checkin_image = None + # S3アップロードが成功した場合も失敗した場合もCheckinImagesレコードを作成 + checkin_image = create_checkin_image_record( + gps_checkin, + s3_result['s3_url'], # S3アップロードが失敗した場合はNone + s3_result['s3_key'], + uploaded_file.name, + exif_data, + request_id, + user + ) + if s3_result['success']: - # CheckinImagesテーブルにレコード作成 - checkin_image = create_checkin_image_record( - gps_checkin, - s3_result['s3_url'], - s3_result['s3_key'], - uploaded_file.name, - exif_data, - request_id, - user - ) + logger.info(f"[BULK_UPLOAD] ✅ Photo uploaded to S3 and CheckinImages record created - ID: {request_id}") + else: + logger.warning(f"[BULK_UPLOAD] ⚠️ S3 upload failed but CheckinImages record created with local path - ID: {request_id}, error: {s3_result['error']}") return { 'gps_checkin': gps_checkin,