aim_server_multipart 0.0.1 copy "aim_server_multipart: ^0.0.1" to clipboard
aim_server_multipart: ^0.0.1 copied to clipboard

Multipart form data parsing for aim_server framework. Supports multipart/form-data format.

aim_server_multipart #

Multipart form data parser for the Aim framework. Handles file uploads and form data with security in mind.

Overview #

aim_server_multipart provides secure and efficient parsing of multipart/form-data requests for the Aim framework. It's designed as a separate package to keep applications that don't need file uploads lightweight.

Features #

  • ๐Ÿ“ค File Upload Support - Handle single and multiple file uploads
  • ๐Ÿ”’ Security First - Automatic filename sanitization prevents path traversal attacks
  • ๐Ÿ“ Size Limits - Configure per-file and total upload size limits
  • ๐ŸŽฏ MIME Type Filtering - Allow only specific file types with wildcard support
  • โšก Stream Processing - Efficient memory usage with streaming
  • ๐Ÿงช Well Tested - Comprehensive test suite with 39 tests
  • ๐Ÿ“‹ RFC 7578 Compliant - Follows the multipart/form-data standard

Installation #

Add aim_server_multipart to your pubspec.yaml:

dependencies:
  aim_server: ^0.0.5
  aim_server_multipart: ^0.0.1

Then run:

dart pub get

Usage #

Basic File Upload #

import 'package:aim_server/aim_server.dart';
import 'package:aim_server_multipart/aim_server_multipart.dart';

void main() async {
  final app = Aim();

  app.post('/upload', (c) async {
    final form = await c.req.multipart();

    final avatar = form.file('avatar');
    if (avatar != null) {
      // Save the file
      await avatar.saveTo('uploads/${avatar.filename}');

      return c.json({
        'uploaded': true,
        'filename': avatar.filename,
        'size': avatar.size,
      });
    }

    return c.json({'error': 'No file uploaded'}, 400);
  });

  await app.serve(port: 8080);
}

Multiple Files and Fields #

app.post('/upload', (c) async {
  final form = await c.req.multipart();

  // Get text fields
  final title = form.field('title');           // String?
  final tags = form.fields('tags');            // List<String>

  // Get multiple files
  final images = form.files('images');         // List<UploadedFile>
  for (final image in images) {
    await image.saveTo('uploads/${image.filename}');
  }

  return c.json({
    'title': title,
    'tags': tags,
    'uploaded': images.length,
  });
});

With Validation #

app.post('/upload', (c) async {
  try {
    final form = await c.req.multipart(
      maxFileSize: 10 * 1024 * 1024,      // 10MB per file
      maxTotalSize: 50 * 1024 * 1024,     // 50MB total
      allowedMimeTypes: ['image/*'],       // Only images
    );

    final avatar = form.file('avatar');
    if (avatar != null) {
      await avatar.saveTo('uploads/${avatar.filename}');
      return c.json({'success': true});
    }

    return c.json({'error': 'No file uploaded'}, 400);
  } on Exception catch (e) {
    // File too large, wrong type, etc.
    return c.json({'error': e.toString()}, 400);
  }
});

Text File Processing #

app.post('/upload-csv', (c) async {
  final form = await c.req.multipart(
    allowedMimeTypes: ['text/csv', 'text/plain'],
  );

  final csvFile = form.file('data');
  if (csvFile != null) {
    // Read as string
    final csvContent = csvFile.asString();
    final rows = csvContent.split('\n');

    return c.json({
      'rows': rows.length,
      'preview': rows.take(5).toList(),
    });
  }

  return c.json({'error': 'No file uploaded'}, 400);
});

API Reference #

MultipartFormData #

Represents parsed multipart form data.

Methods:

  • field(String name) - Get single text field โ†’ String?
  • fields(String name) - Get multiple fields with same name โ†’ List<String>
  • file(String name) - Get single file โ†’ UploadedFile?
  • files(String name) - Get multiple files with same name โ†’ List<UploadedFile>
  • has(String name) - Check if field/file exists โ†’ bool

UploadedFile #

Represents an uploaded file.

Properties:

  • filename - Sanitized safe filename (String)
  • originalFilename - Original filename from client (String?)
  • contentType - MIME type (String)
  • bytes - File contents (List<int>)
  • size - File size in bytes (int)

Methods:

  • saveTo(String path) - Save file to disk
  • asString([Encoding encoding]) - Read file as string

Request Extension #

extension MultipartRequest on Request {
  Future<MultipartFormData> multipart({
    int? maxFileSize,
    int? maxTotalSize,
    List<String>? allowedMimeTypes,
  });
}

Parameters:

  • maxFileSize - Maximum size per file in bytes
  • maxTotalSize - Maximum total size in bytes
  • allowedMimeTypes - List of allowed MIME types
    • Exact match: ['image/png', 'application/pdf']
    • Wildcard: ['image/*', 'video/*']

Security #

Filename Sanitization #

Uploaded filenames are automatically sanitized to prevent security issues:

  • โŒ Path traversal attacks (../../../etc/passwd โ†’ safe filename)
  • โŒ Absolute paths (/tmp/evil.sh โ†’ safe filename)
  • โŒ Dangerous characters removed
  • โœ… Random unique filenames generated (collision-free)
  • โœ… Original filename preserved in originalFilename property

Example generated filename:

file_1234567890_abc123de.jpg

Size Limits #

final form = await c.req.multipart(
  maxFileSize: 10 * 1024 * 1024,  // 10MB limit
);

Throws Exception if limit exceeded.

MIME Type Filtering #

final form = await c.req.multipart(
  allowedMimeTypes: [
    'image/png',
    'image/jpeg',
    'image/*',  // All image types
  ],
);

Throws Exception for disallowed types.

Error Handling #

app.post('/upload', (c) async {
  try {
    final form = await c.req.multipart(
      maxFileSize: 10 * 1024 * 1024,
      allowedMimeTypes: ['image/*'],
    );

    // Process files...

  } on FormatException catch (e) {
    // Invalid Content-Type, missing name parameter, etc.
    return c.json({'error': 'Invalid request: ${e.message}'}, 400);
  } on Exception catch (e) {
    // File too large, wrong MIME type, etc.
    return c.json({'error': e.toString()}, 400);
  }
});

Examples #

See the example directory for complete working examples.

Contributing #

Contributions are welcome! Please see the main repository for contribution guidelines.

License #

See the LICENSE file in the main repository.

References #

1
likes
160
points
52
downloads

Publisher

verified publisheraim-dart.dev

Weekly Downloads

Multipart form data parsing for aim_server framework. Supports multipart/form-data format.

Repository (GitHub)
View/report issues

Documentation

API reference

License

MIT (license)

Dependencies

aim_server, mime

More

Packages that depend on aim_server_multipart