download_util.py 2.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384
  1. import logging
  2. import os
  3. from pathlib import Path
  4. from urllib.parse import urlparse
  5. import httpx
  6. from app.config import settings
  7. logger = logging.getLogger(__name__)
  8. def download_file(url: str, target_dir: str) -> str | None:
  9. if not url or not target_dir:
  10. logger.error("URL or target directory is blank. URL: %s, TargetDir: %s", url, target_dir)
  11. return None
  12. try:
  13. os.makedirs(target_dir, exist_ok=True)
  14. filename = _get_filename_from_url(url)
  15. if not filename:
  16. filename = f"downloaded_file_{os.getpid()}"
  17. filepath = os.path.join(target_dir, filename)
  18. if os.path.isfile(filepath):
  19. logger.info("File already exists, skipping download: %s", filepath)
  20. return filepath
  21. logger.info("Starting download from: %s to: %s", url, filepath)
  22. with httpx.Client(timeout=settings.http_timeout, follow_redirects=True) as client:
  23. resp = client.get(url)
  24. resp.raise_for_status()
  25. with open(filepath, "wb") as f:
  26. f.write(resp.content)
  27. if os.path.isfile(filepath) and os.path.getsize(filepath) > 0:
  28. logger.info("Successfully downloaded file: %s (%d bytes)", filepath, os.path.getsize(filepath))
  29. return filepath
  30. logger.error("Download failed: file not found or empty after download")
  31. return None
  32. except Exception as e:
  33. logger.error("Failed to download file from %s: %s", url, e)
  34. return None
  35. def download_files(urls: list[str], workspace: str, prefix_dir: str | None = None) -> list[str]:
  36. downloaded: list[str] = []
  37. if not urls:
  38. return downloaded
  39. target_dir = workspace + ("/" + prefix_dir if prefix_dir else "")
  40. logger.info("Starting batch download of %d files to directory: %s", len(urls), target_dir)
  41. for url in urls:
  42. try:
  43. local_path = download_file(url, target_dir)
  44. if local_path:
  45. rel = get_relative_path(local_path, workspace)
  46. downloaded.append(rel)
  47. except Exception as e:
  48. logger.error("Failed to download file from URL: %s, %s", url, e)
  49. logger.info("Batch download completed. Successfully downloaded %d/%d files", len(downloaded), len(urls))
  50. return downloaded
  51. def get_relative_path(absolute_path: str, base_dir: str) -> str:
  52. try:
  53. return os.path.relpath(absolute_path, base_dir).replace("\\", "/")
  54. except Exception:
  55. return os.path.basename(absolute_path)
  56. def _get_filename_from_url(url: str) -> str | None:
  57. try:
  58. path = urlparse(url).path
  59. if path:
  60. name = path.rsplit("/", 1)[-1]
  61. if name:
  62. return name
  63. except Exception:
  64. pass
  65. return None