fix(dart): derive InputFile filename from path - #1902
Conversation
Uploads over 5MB switch from MultipartFile.fromPath to fromBytes, and
only fromPath derives a filename when the caller omits one. Every chunk
part therefore went out with no filename in its content-disposition, so
Swoole parsed it as an ordinary form field instead of a file upload,
getFiles('file') came back empty and the server rejected the upload with
storage_file_empty.
Deriving the filename at construction keeps both upload paths sending
the same part. Dart and Flutter share this template, so both are fixed.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
| expect( | ||
| InputFile.fromPath(path: '/path/to/video.mp4').filename, | ||
| 'video.mp4', | ||
| reason: 'chunks of a file over 5MB are sent as bytes, and without a ' | ||
| 'filename the server rejects them as an empty file', | ||
| ); | ||
| expect(InputFile.fromPath(path: './relative/image.png').filename, | ||
| 'image.png'); | ||
| expect(InputFile.fromPath(path: 'video.mp4').filename, 'video.mp4'); | ||
| }); |
There was a problem hiding this comment.
These tests only inspect the intermediate InputFile.filename field. They never exercise the chunked multipart path or verify that Content-Disposition contains a filename. If chunkedUpload stops forwarding this field to MultipartFile.fromBytes, all these tests will still pass while uploads over 5 MB fail as empty files again. The repository requires tests to verify observable behavior instead of mirroring implementation state, so this must be addressed before merging.
Context Used: Call out and harshly judge implementation-coupled tests. We don't mirror source code, configuration, or version pins in assertions. We test observable behavior; use linters for syntax and schema checks. (source)
Prompt To Fix With AI
This is a comment left during a code review.
Path: templates/dart/test/src/input_file_test.dart.twig
Line: 43-52
Comment:
**Tests miss upload behavior**
These tests only inspect the intermediate `InputFile.filename` field. They never exercise the chunked multipart path or verify that `Content-Disposition` contains a filename. If `chunkedUpload` stops forwarding this field to `MultipartFile.fromBytes`, all these tests will still pass while uploads over 5 MB fail as empty files again. The repository requires tests to verify observable behavior instead of mirroring implementation state, so this must be addressed before merging.
**Context Used:** Call out and harshly judge implementation-coupled tests. We don't mirror source code, configuration, or version pins in assertions. We test observable behavior; use linters for syntax and schema checks. ([source](https://app.greptile.com/review/custom-context?memory=instruction-0))
---
For each issue above, determine whether it is valid and should be fixed. If so, fix it directly.Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!
Summary
Uploads over 5 MB from the Flutter and Dart SDKs fail with
storage_file_empty/ "Empty file passed to the endpoint." whenever the caller usesInputFile.fromPath(path: ...)without passingfilename:. Files at or under 5 MB upload fine, which is what makes it look like a chunking problem.It isn't. The SDK chunks correctly. The two upload paths just build the multipart part differently:
content-dispositionsentMultipartFile.fromPath(...)name="file"; filename="video.mp4"MultipartFile.fromBytes(..., filename: file.filename)name="file"InputFile.fromPathleavesfilenamenull when the caller omits it.package:http's IO helper doesfilename ??= segments.last, so the single-request path always ends up with a filename, whilefromBytespasses the null straight through andpackage:httpemits the attribute onlyif (file.filename != null).A multipart part with no filename is not a file upload. Swoole puts it in
$request->post, so$request->getFiles('file')returns[]andCreate.phpthrowsSTORAGE_FILE_EMPTY. Every chunk is rejected.Fix
Derive the filename from the path at construction, the same way
package:httpalready does forMultipartFile.fromPath, so both upload paths send an identical part. Dart and Flutter share this template, so one change covers both SDKs. A path with no final segment (/path/to/) still leavesfilenamenull, so nothing starts sendingfilename="".Verification
Driving the real generated SDK at a Swoole server that reproduces Appwrite's
getFiles('file')check, uploading 12 MB viaInputFile.fromPath(path: ...)with nofilename:Before:
After:
The unit tests were also seen red against the pre-fix template, failing on exactly the null filename:
Generationsuite: 87 passed, 4 skipped.lint-twig: 586 files, 0 errors. Generated Dart SDK passesdart analyzewith no issues.Notes
expect(inputFile.filename, isNull), which pinned the broken behaviour. It is updated rather than removed.InputFile.fromBytesalready requires a filename.filename:explicitly.composer lint(phpcs) reports 219 pre-existing errors across 21 PHP files onmain. This PR changes no PHP and leaves them alone.🤖 Generated with Claude Code