file_upload_task.py 5.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132
  1. import logging
  2. import os
  3. import time
  4. from datetime import datetime
  5. from typing import Any
  6. from app.service.task.base_task import BaseTask
  7. from app.service.task.task_param import TaskParam
  8. from app.service.task.task_result import TaskResult
  9. from app.util import aliyun_util, google_storage_util
  10. from app.util.download_util import download_file
  11. from app.util.file_util import generate_unique_filename
  12. logger = logging.getLogger(__name__)
  13. class FileUploadTask(BaseTask):
  14. def get_type(self) -> str:
  15. return "file_upload"
  16. def execute(self, param: TaskParam) -> TaskResult:
  17. files_obj = param.inputs.get("files")
  18. files: list[str] = files_obj if isinstance(files_obj, list) else []
  19. try:
  20. provider = str(param.inputs.get("provider", ""))
  21. prefix = str(param.inputs.get("prefix", ""))
  22. date_folder = param.inputs.get("date_folder")
  23. logger.info("[file_upload] Starting: %d files, provider=%s, prefix=%s, date_folder=%s",
  24. len(files), provider, prefix, date_folder)
  25. if not files:
  26. return TaskResult.failure("No files to upload")
  27. if provider not in ("aliyun", "google"):
  28. return TaskResult.failure(f"Unsupported target provider: {provider}")
  29. if not prefix or not prefix.strip():
  30. return TaskResult.failure("Key prefix cannot be empty")
  31. if not prefix.endswith("/"):
  32. prefix += "/"
  33. final_prefix = self._build_final_prefix(prefix, str(date_folder) if date_folder else None)
  34. logger.info("[file_upload] Final prefix: %s", final_prefix)
  35. t0 = time.time()
  36. local_files = self._process_files(files, param.workspace, param.step_id)
  37. logger.info("[file_upload] Resolved %d local files in %.2fs", len(local_files), time.time() - t0)
  38. uploaded_urls: list[str] = []
  39. upload_t0 = time.time()
  40. if provider == "aliyun":
  41. uploaded_urls = self._upload_to_aliyun(local_files, final_prefix)
  42. elif provider == "google":
  43. uploaded_urls = self._upload_to_google(local_files, final_prefix)
  44. logger.info("[file_upload] Completed: %d/%d files uploaded to %s in %.2fs",
  45. len(uploaded_urls), len(local_files), provider, time.time() - upload_t0)
  46. return TaskResult.success({"files": uploaded_urls})
  47. except Exception as e:
  48. logger.error("[file_upload] Execution failed: %s", e, exc_info=True)
  49. return TaskResult.failure(f"File copy failed: {e}")
  50. @staticmethod
  51. def _build_final_prefix(prefix: str, date_folder: str | None) -> str:
  52. if not date_folder:
  53. return prefix
  54. try:
  55. date_path = datetime.now().strftime(date_folder)
  56. if not date_path.endswith("/"):
  57. date_path += "/"
  58. return prefix + date_path
  59. except Exception:
  60. return prefix
  61. @staticmethod
  62. def _process_files(files: list[str], workspace: str, step_id: str) -> list[str]:
  63. local_files: list[str] = []
  64. for f in files:
  65. try:
  66. if _is_url(f):
  67. logger.info("[file_upload] Downloading URL: %s", f)
  68. local_path = download_file(f, workspace + "/" + step_id)
  69. if local_path:
  70. local_files.append(local_path)
  71. else:
  72. local_files.append(workspace + "/" + f)
  73. except Exception as e:
  74. logger.error("[file_upload] Error processing file: %s, %s", f, e)
  75. return local_files
  76. @staticmethod
  77. def _upload_to_aliyun(files: list[str], prefix: str) -> list[str]:
  78. uploaded: list[str] = []
  79. for path in files:
  80. if not os.path.isfile(path):
  81. logger.warning("[file_upload] File not found, skipping: %s", path)
  82. continue
  83. try:
  84. unique_name = generate_unique_filename(os.path.basename(path))
  85. key = prefix + unique_name
  86. logger.info("[file_upload] Uploading to Aliyun OSS: %s -> %s", os.path.basename(path), key)
  87. url = aliyun_util.upload_file(path, key)
  88. if url:
  89. uploaded.append(url)
  90. except Exception as e:
  91. logger.error("[file_upload] Exception uploading file: %s, %s", path, e)
  92. return uploaded
  93. @staticmethod
  94. def _upload_to_google(files: list[str], prefix: str) -> list[str]:
  95. uploaded: list[str] = []
  96. for path in files:
  97. if not os.path.isfile(path):
  98. logger.warning("[file_upload] File not found, skipping: %s", path)
  99. continue
  100. try:
  101. unique_name = generate_unique_filename(os.path.basename(path))
  102. key = prefix + unique_name
  103. logger.info("[file_upload] Uploading to Google GCS: %s -> %s", os.path.basename(path), key)
  104. url = google_storage_util.upload_file(path, key)
  105. if url:
  106. uploaded.append(url)
  107. except Exception as e:
  108. logger.error("[file_upload] Exception uploading file: %s, %s", path, e)
  109. return uploaded
  110. def _is_url(s: str) -> bool:
  111. if not s:
  112. return False
  113. lower = s.strip().lower()
  114. return any(lower.startswith(p) for p in ("http://", "https://", "ftp://", "s3://", "gs://"))