oss_upload.py 3.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127
  1. import logging
  2. import os
  3. from pathlib import Path
  4. from typing import Optional
  5. try:
  6. import alibabacloud_oss_v2 as oss
  7. except Exception: # pragma: no cover - import guard for optional dependency
  8. oss = None
  9. logger = logging.getLogger("oss.upload")
  10. class OssUploadError(RuntimeError):
  11. pass
  12. def _get_env(*keys: str, required: bool = False) -> Optional[str]:
  13. for key in keys:
  14. value = os.getenv(key)
  15. if value:
  16. return value
  17. if required:
  18. raise OssUploadError(f"missing env: {keys[0]}")
  19. return None
  20. def _build_object_key(
  21. html_path: Path,
  22. object_key: Optional[str],
  23. prefix: Optional[str],
  24. task_id: Optional[str],
  25. ) -> str:
  26. if object_key:
  27. key = object_key
  28. elif task_id:
  29. key = f"{task_id}.html"
  30. else:
  31. key = html_path.name
  32. if prefix:
  33. prefix_clean = prefix.strip().strip("/")
  34. if prefix_clean:
  35. return f"{prefix_clean}/{key.lstrip('/')}"
  36. return key.lstrip("/")
  37. def upload_html_to_oss(
  38. html_path: Path,
  39. *,
  40. object_key: Optional[str] = None,
  41. task_id: Optional[str] = None,
  42. ) -> str:
  43. """
  44. Upload a generated HTML file to OSS and return a public URL.
  45. """
  46. if not html_path.exists():
  47. raise OssUploadError(f"html not found: {html_path}")
  48. if html_path.suffix.lower() != ".html":
  49. raise OssUploadError(f"invalid html suffix: {html_path}")
  50. if oss is None:
  51. raise OssUploadError("alibabacloud-oss-v2 not installed")
  52. access_key_id = _get_env("ALIYUN_OSS_ACCESS_KEY_ID")
  53. access_key_secret = _get_env("ALIYUN_OSS_ACCESS_KEY_SECRET")
  54. if not access_key_id:
  55. raise OssUploadError("missing env: ALIYUN_OSS_ACCESS_KEY_ID")
  56. if not access_key_secret:
  57. raise OssUploadError("missing env: ALIYUN_OSS_ACCESS_KEY_SECRET")
  58. bucket_name = _get_env("ALIYUN_OSS_BUCKET")
  59. if not bucket_name:
  60. raise OssUploadError("missing env: ALIYUN_OSS_BUCKET")
  61. region = _get_env("ALIYUN_OSS_REGION")
  62. if not region:
  63. raise OssUploadError("missing env: ALIYUN_OSS_REGION")
  64. prefix = _get_env("ALIYUN_OSS_PREFIX")
  65. if not prefix:
  66. raise OssUploadError("missing env: ALIYUN_OSS_PREFIX")
  67. public_base_url = _get_env("ALIYUN_OSS_PUBLIC_BASE_URL")
  68. if not public_base_url:
  69. raise OssUploadError("missing env: ALIYUN_OSS_PUBLIC_BASE_URL")
  70. key = _build_object_key(html_path, object_key, prefix, task_id)
  71. logger.info(
  72. "oss upload start",
  73. extra={
  74. "bucket": bucket_name,
  75. "region": region,
  76. "key": key,
  77. "html_path": str(html_path),
  78. },
  79. )
  80. credentials_provider = oss.credentials.StaticCredentialsProvider(
  81. access_key_id=access_key_id,
  82. access_key_secret=access_key_secret,
  83. )
  84. cfg = oss.config.load_default()
  85. cfg.credentials_provider = credentials_provider
  86. cfg.region = region
  87. client = oss.Client(cfg)
  88. request = oss.PutObjectRequest(bucket=bucket_name, key=key)
  89. result = client.put_object_from_file(request, str(html_path))
  90. if result.status_code >= 300:
  91. logger.error(
  92. "oss upload failed",
  93. extra={
  94. "bucket": bucket_name,
  95. "region": region,
  96. "key": key,
  97. "status_code": result.status_code,
  98. },
  99. )
  100. raise OssUploadError(f"oss upload failed: status={result.status_code}")
  101. logger.info(
  102. "oss upload success",
  103. extra={
  104. "bucket": bucket_name,
  105. "region": region,
  106. "key": key,
  107. "status_code": result.status_code,
  108. },
  109. )
  110. base = public_base_url.rstrip("/")
  111. return f"{base}/{key}"