download_util.py 2.7 KB

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