from qiniu import Auth, put_file_v2, etag, BucketManager import qiniu.config import os import uuid from typing import Optional, Tuple from app.core.config import QINIU_ACCESS_KEY, QINIU_SECRET_KEY, QINIU_BUCKET, QINIU_DOMAIN class QiniuService: def __init__(self): self.access_key = QINIU_ACCESS_KEY self.secret_key = QINIU_SECRET_KEY self.bucket_name = QINIU_BUCKET self.domain = QINIU_DOMAIN if not self.access_key or not self.secret_key: print("ERROR: 七牛云Access Key或Secret Key为空!") if not self.bucket_name: print("ERROR: 七牛云Bucket名称为空!") if not self.domain: print("ERROR: 七牛云Domain为空!") self.q = Auth(self.access_key, self.secret_key) def upload_audio_file(self, file_path: str, meeting_id: int, original_filename: str) -> Tuple[bool, str, Optional[str]]: """ Upload audio file to Qiniu cloud storage Args: file_path: Local file path meeting_id: Meeting ID for directory structure original_filename: Original filename Returns: Tuple of (success: bool, url: str, error_message: Optional[str]) """ try: # Validate file exists if not os.path.exists(file_path): return False, "", f"File not found: {file_path}" file_extension = os.path.splitext(original_filename)[1] unique_filename = f"{uuid.uuid4()}{file_extension}" key = f"record/{meeting_id}/{unique_filename}" # Generate upload token token = self.q.upload_token(self.bucket_name, key, 3600) print(f"DEBUG: 生成音频上传token成功, key: {key}") print(f"DEBUG: token前50位: {token[:50] if token else 'None'}") # Upload file with retry mechanism ret, info = put_file_v2(token, key, file_path) print(f"DEBUG: Qiniu upload response - ret: {ret}, info: {info}") print(f"DEBUG: Qiniu upload info details - status_code: {info.status_code}, text_body: {info.text_body}") # Check if upload was successful # For put_file_v2, we need to check the info.status_code if info.status_code == 200 and ret and 'key' in ret and ret['key'] == key: url = f"http://{self.domain}/{key}" return True, url, None else: # Extract error information properly error_msg = f"Upload failed: status_code={info.status_code}" if hasattr(info, 'text_body') and info.text_body: error_msg += f", response={info.text_body}" return False, "", error_msg except Exception as e: import traceback traceback.print_exc() return False, "", f"Upload error: {str(e)}" def upload_markdown_image(self, file_path: str, meeting_id: int, original_filename: str) -> Tuple[bool, str, Optional[str]]: """ Upload markdown image to Qiniu cloud storage Args: file_path: Local file path meeting_id: Meeting ID for directory structure original_filename: Original filename Returns: Tuple of (success: bool, url: str, error_message: Optional[str]) """ try: # Validate file exists if not os.path.exists(file_path): return False, "", f"File not found: {file_path}" file_extension = os.path.splitext(original_filename)[1] unique_filename = f"{uuid.uuid4()}{file_extension}" key = f"markdown/{meeting_id}/{unique_filename}" # Generate upload token token = self.q.upload_token(self.bucket_name, key, 3600) # Upload file with retry mechanism ret, info = put_file_v2(token, key, file_path) print(f"DEBUG: Qiniu image upload response - ret: {ret}, info: {info}") print(f"DEBUG: Qiniu image upload info details - status_code: {info.status_code}, text_body: {info.text_body}") # Check if upload was successful # For put_file_v2, we need to check the info.status_code if info.status_code == 200 and ret and 'key' in ret and ret['key'] == key: url = f"http://{self.domain}/{key}" return True, url, None else: # Extract error information properly error_msg = f"Upload failed: status_code={info.status_code}" if hasattr(info, 'text_body') and info.text_body: error_msg += f", response={info.text_body}" return False, "", error_msg except Exception as e: import traceback traceback.print_exc() return False, "", f"Upload error: {str(e)}" def delete_file(self, key: str) -> Tuple[bool, Optional[str]]: """ Delete file from Qiniu cloud storage Args: key: File key in Qiniu storage Returns: Tuple of (success: bool, error_message: Optional[str]) """ try: from qiniu import BucketManager bucket = BucketManager(self.q) ret, info = bucket.delete(self.bucket_name, key) if ret is None: return True, None else: return False, f"Delete failed: {info}" except Exception as e: return False, f"Delete error: {str(e)}" # Global instance qiniu_service = QiniuService()