Fix A1 FTP uploads by replacing storbinary with manual transfer

The A1 printer's FTP server hangs when Python's storbinary() calls
voidresp() to wait for the server's completion response. This caused
upload timeouts on A1 and A1 Mini printers.

Fix contributed by an A1 user - replaces storbinary() with manual
chunked transfer using transfercmd() + sendall():
- Uses 1MB chunks (CHUNK_SIZE constant) for better throughput
- Sets explicit 120s socket timeout on data connection
- Manually closes connection after transfer, avoiding voidresp() hang

Applied to all printer models since the manual approach is compatible
with X1C/P1S/P1P as well (transfercmd is what storbinary uses internally).
This commit is contained in:
maziggy
2026-02-05 17:06:15 +01:00
parent 8b3ea0602c
commit 5da769be31
+50 -9
View File
@@ -80,6 +80,9 @@ class BambuFTPClient:
# Models that may need SSL mode fallback (try prot_p first, fall back to prot_c)
# These models have varying FTP SSL behavior depending on firmware version
A1_MODELS = ("A1", "A1 Mini")
# Chunk size for manual upload transfer (1MB)
# Larger chunks reduce overhead and work better with A1 printers
CHUNK_SIZE = 1024 * 1024
# Cache for working FTP modes per printer IP
# Maps IP -> "prot_p" or "prot_c"
@@ -363,15 +366,37 @@ class BambuFTPClient:
uploaded = 0
def on_block(block: bytes):
nonlocal uploaded
uploaded += len(block)
if progress_callback:
progress_callback(uploaded, file_size)
# Use manual transfer instead of storbinary() for A1 compatibility
# A1 printers have issues with storbinary's voidresp() hanging after transfer
with open(local_path, "rb") as f:
logger.debug(f"FTP STOR command starting for {remote_path}")
self._ftp.storbinary(f"STOR {remote_path}", f, callback=on_block)
conn = self._ftp.transfercmd(f"STOR {remote_path}")
# Set explicit socket options for reliable transfer
conn.setblocking(True)
conn.settimeout(120) # 2 minute timeout per chunk
try:
while True:
chunk = f.read(self.CHUNK_SIZE)
if not chunk:
logger.debug("FTP upload: final chunk reached")
break
conn.sendall(chunk)
uploaded += len(chunk)
logger.debug(f"FTP upload progress: {uploaded}/{file_size} bytes")
if progress_callback:
progress_callback(uploaded, file_size)
except OSError as e:
logger.error(f"FTP connection lost during upload: {e}")
conn.close()
raise
conn.close()
logger.info(f"FTP upload complete: {remote_path}")
return True
except ftplib.error_perm as e:
@@ -399,8 +424,24 @@ class BambuFTPClient:
return False
try:
buffer = BytesIO(data)
self._ftp.storbinary(f"STOR {remote_path}", buffer)
# Use manual transfer instead of storbinary() for A1 compatibility
conn = self._ftp.transfercmd(f"STOR {remote_path}")
conn.setblocking(True)
conn.settimeout(120)
try:
# Send data in chunks
offset = 0
while offset < len(data):
chunk = data[offset : offset + self.CHUNK_SIZE]
conn.sendall(chunk)
offset += len(chunk)
except OSError as e:
logger.error(f"FTP connection lost during upload_bytes: {e}")
conn.close()
raise
conn.close()
return True
except Exception:
return False