Skip to main content

2026-09-03-cloud-upload-server-names-the-object


title: "Uploads: the server names the object, and the caller passes a folder and an extension" affects: dartway_serverpod_core_server: "0.12.0" dartway_serverpod_core_flutter: "0.12.0"

Who is affected

Any project that uploads files — through DwFileUploadHandler, or by calling the upload endpoints itself.

The caller used to hand the server the whole object key and the server signed a policy for exactly it. requireLogin was the entire check: nothing tested that the key was free or that it belonged to the caller, so a signed-in user who knew somebody else's key overwrote the object behind it, and object storage reports an overwrite on neither side. verifyUpload completed the picture — it accepted any path and recorded the caller as the owner, so an object could be claimed without uploading a byte.

The caller now names neither. It passes the folder, the extension of the bytes actually being sent and optionally a readable file name; the server builds <folder>/u<userId>/<stamp>_<name><ext>, records the reservation, and afterwards accepts only the reservation it issued.

What to change

1. Call sites of uploadBytesToServer / uploadBytesToServerUrl. path was the whole key and is now three parameters:

- DwFileUploadHandler.uploadBytesToServer(bytes: bytes, path: 'avatars/$name.jpg');
+ DwFileUploadHandler.uploadBytesToServer(
+ bytes: bytes,
+ folder: 'avatars',
+ fileExtension: '.jpg',
+ fileName: name, // optional, only so the object downloads readably
+ );

fileExtension is separate from fileName because the two disagree: an image picked as HEIC is uploaded as JPEG, and the server reads the recorded mime type off the key it builds.

2. defaultUploadNameTemplate and defaultPlatformUploadNameTemplate are gone. A project that overrode either was naming the object, which is what moved to the server. What survives of that intent is fileName.

3. pickAndUploadImage / pickAndUploadImageUrl take an optional path. Nothing breaks without it, but until now every picked image landed in the bucket root — if the project worked around that by not using these calls, it can stop.

4. A project with its own DwServerTransport implementation must follow the interface: getUploadDescription({folder, fileExtension, fileName}) now answers a DwUploadTicket (fileId + uploadDescription), and verifyUpload({fileId}) replaces verifyUpload({path}).

5. The schema moved with it — see the migrations note dated the same day; DwCloudFile gains fileName and a unique index, and applying it needs a check first.

What does not change

Objects already in the bucket do not move, and publicUrl values already stored keep resolving. What changed is how new keys are built.

How to check

dart analyze catches every call site — all four items above are compile errors, not silent drift. Then upload one file of each kind the app handles (a picked photo, a picked document) and confirm it lands under <folder>/u<userId>/… and comes back with a working publicUrl.