Search code examples
dartaqueduct

How can I write uploaded multipart files to disk?


I'm trying to handle file upload through multipart requests with Aqueduct. Aqueduct has now an example on how to handle multipart requests here:

https://aqueduct.io/docs/http/request_and_response/#example-multipartform-data

The example explains, how to get the header and the content of the files. However it doesn't explain how to write the content into a file on disk.

How can I write the content of the files uploaded to disk?

Below an example that shows what I want to achieve, but doesn't work:

import 'dart:io';

import 'package:aqueduct/aqueduct.dart';
import 'package:mime/mime.dart';

class MediaUploadController extends ResourceController {
  MediaUploadController() {
    acceptedContentTypes = [ContentType("multipart", "form-data")];
  }

  @Operation.post()
  Future<Response> postMultipartForm() async {
    final transformer = MimeMultipartTransformer(request.raw.headers.contentType.parameters["boundary"]);
    final bodyStream = Stream.fromIterable([await request.body.decode<List<int>>()]);
    final parts = await transformer.bind(bodyStream).toList();

    for (var part in parts) {
      final String contentType = part.headers["content-type"];

      // Write content to disk
      final content = await part.toList();

      final fileName = DateTime.now().millisecondsSinceEpoch.toString() + ".jpg";
      var file = new File('data/' + fileName);
      var sink = file.openWrite();
      sink.write(content);
      sink.close();
    }

    return new Response.ok({});   
  }
}

Solution

  • This below actually worked. Additionally to the mime package, I have also added the http_server package to pubspec.yaml, because it makes it easier to handle the multipart form data.

    dependencies:
      aqueduct: ^3.0.1
      mime: ^0.9.6+2
      http_server: ^0.9.8+1
    

    Then I studied some other frameworks to see how they handled writing to the file. It's so complicated to get how this multipart stuff and streams work together. But at last after nearly a week, the light at the end of the tunnel... until the next questions pop up. Most likely 10 minutes down the line :)

    import 'dart:io';
    
    import 'package:aqueduct/aqueduct.dart';
    import 'package:mime/mime.dart';
    import 'package:http_server/http_server.dart';
    
    class MediaUploadController extends ResourceController {
      MediaUploadController() {
        acceptedContentTypes = [ContentType("multipart", "form-data")];
      }
    
      @Operation.post()
      Future<Response> postMultipartForm() async {
        final transformer = MimeMultipartTransformer(request.raw.headers.contentType.parameters["boundary"]);
        final bodyStream = Stream.fromIterable([await request.body.decode<List<int>>()]);
        final parts = await transformer.bind(bodyStream).toList();
    
        for (var part in parts) {
          HttpMultipartFormData multipart = HttpMultipartFormData.parse(part);
    
          final ContentType contentType = multipart.contentType;
    
          final content = multipart.cast<List<int>>();
    
          final filePath = "data/" + DateTime.now().millisecondsSinceEpoch.toString() + ".jpg";
    
          IOSink sink = File(filePath).openWrite();
          await for (List<int> item in content) {
            sink.add(item);
          }
          await sink.flush();
          await sink.close();
        }
    
        return new Response.ok({});   
      }
    }