import os import urequests from sd_util import SDCardManager def download_file(url, dest_filename, use_sd=True): """Downloads a file from a URL over the network in memory-efficient chunks. If use_sd is True, it automatically mounts the microSD card and saves to /sd/dest_filename. Otherwise, it saves to the local flash storage. Returns: str: The full path to the downloaded file on success, None on failure. """ dest_path = dest_filename sd_manager = None if use_sd: sd_manager = SDCardManager(mount_point='/sd') if not sd_manager.mount(): print("Download Error: Could not mount SD card.") return None dest_path = f"/sd/{dest_filename}" print(f"Starting download from: {url} -> {dest_path}") try: res = urequests.get(url, stream=True) except Exception as e: print(f"HTTP Connection failed: {e}") return None if res.status_code != 200: print(f"HTTP Error: Received status code {res.status_code}") res.close() return None try: # Read from socket stream in chunks to avoid OutOfMemory errors chunk_size = 4096 total_downloaded = 0 with open(dest_path, 'wb') as f: while True: # Read chunk from the raw socket connection stream chunk = res.raw.read(chunk_size) if not chunk: break f.write(chunk) total_downloaded += len(chunk) # Dynamic logging if total_downloaded % (chunk_size * 25) == 0: print(f"Downloaded {total_downloaded // 1024} KB...") print(f"Download complete! Saved {total_downloaded} bytes to '{dest_path}'.") return dest_path except Exception as e: print(f"Error writing to file: {e}") # Clean up partial file on failure try: os.remove(dest_path) except: pass return None finally: res.close()