Basics / Body Parsing

File Uploads

Archery makes inspecting and saving uploaded files extremely straightforward. The framework automatically parses multipart/form-data requests and exposes file data through robust UploadedFile objects.

Retrieving Uploaded Files

You can access files submitted via a form request using the file method on the FormRequest wrapper.

router.post('/avatar/upload', (request) async {
  final form = request.form();
  
  final avatar = await form.file('avatar');
  
  if (avatar == null || !avatar.isValid) {
    request.flash(key: 'error', message: 'Please provide a valid file.');
    return request.redirectBack();
  }
});

To retrieve an array containing all uploaded files, use await form.files().

Inspecting the File

The UploadedFile object contains various helpers for inspecting the file’s properties before you decide to persist it.

// The original name of the file
final String name = avatar.filename;

// The file extension (e.g., 'png')
final String extension = avatar.extension;

// Pre-flight checks for media types
final bool isImage = avatar.isImage;
final bool isVideo = avatar.isVideo;
final bool isAudio = avatar.isAudio;

// Get the actual size in bytes
final int sizeInBytes = await avatar.length;

Storing Files Locally

You can persist the UploadedFile directly to a specific disk location, or use the framework's convenience paths.

Private & Public Disk Contexts

Archery exposes helper methods to save your uploads securely.

// Saves with an auto-generated UUID to lib/src/storage/private/documents/
await document.saveToPrivateDir('documents');

// Saves with an auto-generated UUID to lib/src/http/public/avatars/
await avatar.saveToPublicDir('avatars');

If you prefer to keep the original filename instead of generating a UUID, pass autoName: false:

await document.saveToPrivateDir('documents', autoName: false);

Absolute Path

If you need to define the absolute directory path, simply pass it to the .save() method:

await avatar.save('/var/www/uploads/custom_name.png');

Storing Files on Amazon S3

Archery includes native S3 abstractions. UploadedFile can seamlessly stream its byte payload directly to your S3 bucket without forcing large files into expensive temporary disk I/O.

final objectKey = await avatar.streamToS3(
  acl: S3Acl.publicRead, 
  autoName: true
);

if (objectKey != null) {
  print('Uploaded to S3: $objectKey');
}

Note: You must have your AWS environment variables configured properly to leverage this feature. Review the AWS Integration section for setup details.

File Properties & Streaming

For lower-level use cases, you can access the raw bytes, interpret the text buffer, or stream it to another destination.

// Read text files easily
final content = await textFile.string;

// Get raw Uint8List buffer
final bytes = await avatar.bytes;

// Stream to a custom StreamSink
final controller = StreamController<List<int>>();
await avatar.streamTo(controller.sink);

You can even stream a file directly to the HTTP response, which is incredibly useful for delivering media equipped with Accept-Ranges support for quick scrubbing in the browser:

router.get('/video/stream', (request) async {
  final videoFile = await retrieveStoredFile();
  await videoFile.streamToResponse(request);
});