Upload progress

Co-Authored-By: Claude Opus 5.5 <[email protected]>
This commit is contained in:
sarahandClaude Opus 5.5 committed 2026-10-13 13:37:47 +02:00
1 parent db81507b7c
commit 843ad1986e
9 files changed
+259 -78

No files matched your search

+30 -4
View File
@@ -5,6 +5,7 @@ entries shaped like the server's /api/tree items, so the rest of the client
never needs to know which one it's talking to."""
import fnmatch
import io
import shutil
import uuid
from dataclasses import dataclass
@@ -412,7 +413,8 @@ class ServerSource(Source):
Params = self._At(Root, DirectoryPath)
if Size <= ChunkedUploadThresholdBytes:
with LocalFile.open("rb") as Handle:
Result = self._Json("POST", "/upload", params=Params, files=[("Files", (LocalFile.name, Handle))])
Sending = _CountingReader(Handle, Size, OnProgress) if OnProgress else Handle
Result = self._Json("POST", "/upload", params=Params, files=[("Files", (LocalFile.name, Sending))])
if Result.get("Errors"):
raise SourceError("; ".join(Result["Errors"]))
return Result["Saved"][0]
@@ -423,10 +425,13 @@ class ServerSource(Source):
for ChunkIndex in range(TotalChunks):
ChunkParams = {**Params, "UploadId": UploadId, "ChunkIndex": ChunkIndex,
"TotalChunks": TotalChunks, "FileName": LocalFile.name}
self._Request("POST", "/upload/chunk", params=ChunkParams,
files=[("Chunk", ("chunk", Handle.read(UploadChunkSizeBytes)))])
Chunk = Handle.read(UploadChunkSizeBytes)
Sending = io.BytesIO(Chunk)
if OnProgress:
OnProgress((ChunkIndex + 1) / TotalChunks)
Before = ChunkIndex * UploadChunkSizeBytes
Sending = _CountingReader(Sending, len(Chunk),
lambda Fraction, B=Before, L=len(Chunk): OnProgress((B + L * Fraction) / Size))
self._Request("POST", "/upload/chunk", params=ChunkParams, files=[("Chunk", ("chunk", Sending))])
Done = {**Params, "UploadId": UploadId, "TotalChunks": TotalChunks, "FileName": LocalFile.name}
return self._Json("POST", "/upload/complete", params=Done)["Saved"]
@@ -474,5 +479,26 @@ class ServerSource(Source):
return self._Request("POST", "/diagram", json=Body).content
class _CountingReader:
"""A file handle that reports how much of it has been read (sent): httpx
reads an upload's file in pieces, so this is its progress."""
def __init__(self, Handle, Size: int, OnProgress: Callable[[float], None]):
self._Handle = Handle
self._Size = Size
self._OnProgress = OnProgress
self._Sent = 0
def read(self, Count: int = -1) -> bytes:
Data = self._Handle.read(Count)
self._Sent += len(Data)
if self._Size:
self._OnProgress(min(1.0, self._Sent / self._Size))
return Data
def __getattr__(self, Name: str):
return getattr(self._Handle, Name) # seek, tell, fileno: for the length
def MakeSource(Config: AppConfig, Token: str = "") -> Source:
return ServerSource(Config, Token) if Config.Mode == "Server" else LocalSource(Config)