flutterretrofitmultipartdio

Flutter Retrofit upload multipart throws 415 Unsupported Media Type


I'm trying to upload an image using Multipart from Flutter Retrofit using this code

  @POST("apiUrl/upload/files")
  @MultiPart()
  Future<TSSuccessResponse<UploadFileRemoteResponse>> uploadFile(
      @Part() File file);

However it keeps getting error 415 that says application/form-octet is not supported MediaType.

E/flutter ( 8367): [ERROR:flutter/runtime/dart_vm_initializer.cc(41)] Unhandled Exception: DioError [bad response]: The request returned an invalid status code of 415.
E/flutter ( 8367): #0      DioMixin.fetch.<anonymous closure> (package:dio/src/dio_mixin.dart:530:7)
E/flutter ( 8367): #1      _RootZone.runBinary (dart:async/zone.dart:1665:54)
E/flutter ( 8367): #2      _FutureListener.handleError (dart:async/future_impl.dart:162:22)
E/flutter ( 8367): #3      Future._propagateToListeners.handleError (dart:async/future_impl.dart:779:47)
E/flutter ( 8367): #4      Future._propagateToListeners (dart:async/future_impl.dart:800:13)
E/flutter ( 8367): #5      Future._completeError (dart:async/future_impl.dart:575:5)
E/flutter ( 8367): #6      _SyncCompleter._completeError (dart:async/future_impl.dart:51:12)
E/flutter ( 8367): #7      _Completer.completeError (dart:async/future_impl.dart:23:5)
E/flutter ( 8367): #8      Future.any.onError (dart:async/future.dart:617:45)
E/flutter ( 8367): #9      _RootZone.runBinary (dart:async/zone.dart:1665:54)
E/flutter ( 8367): #10     _FutureListener.handleError (dart:async/future_impl.dart:162:22)
E/flutter ( 8367): #11     Future._propagateToListeners.handleError (dart:async/future_impl.dart:779:47)
E/flutter ( 8367): #12     Future._propagateToListeners (dart:async/future_impl.dart:800:13)
E/flutter ( 8367): #13     Future._completeError (dart:async/future_impl.dart:575:5)
E/flutter ( 8367): #14     Future._asyncCompleteError.<anonymous closure> (dart:async/future_impl.dart:666:7)
E/flutter ( 8367): #15     _microtaskLoop (dart:async/schedule_microtask.dart:40:21)
E/flutter ( 8367): #16     _startMicrotaskLoop (dart:async/schedule_microtask.dart:49:5)


I/flutter ( 8367): ╔ DioErrorType.badResponse
I/flutter ( 8367): ║    {
I/flutter ( 8367): ║         statusCode: 415,
I/flutter ( 8367): ║         "File type application/octet-stream is not matching: image, pdf, powerpoint, prese
I/flutter ( 8367): ║         ntation, video, video/quicktime"
I/flutter ( 8367): ║         error: "Unsupported Media Type"
I/flutter ( 8367): ║    }
I/flutter ( 8367): ╚══════════════════════════════════════════════════════════════════════════════════════════╝

Then I tried to change the request to include content-type in the @Part() tag as someone mentioned, so that it satisfies the generated file from Retrofit.

  @POST("apiUrl/upload/files")
  @MultiPart()
  Future<TSSuccessResponse<UploadFileRemoteResponse>> uploadFile(
      @Part(contentType: "image/png") File file);

But it throw another error, that is

E/flutter ( 8367): [ERROR:flutter/runtime/dart_vm_initializer.cc(41)] Unhandled Exception: DioError [bad response]: The request returned an invalid status code of 415.
E/flutter ( 8367): #0      DioMixin.fetch.<anonymous closure> (package:dio/src/dio_mixin.dart:530:7)
E/flutter ( 8367): #1      _RootZone.runBinary (dart:async/zone.dart:1665:54)
E/flutter ( 8367): #2      _FutureListener.handleError (dart:async/future_impl.dart:162:22)
E/flutter ( 8367): #3      Future._propagateToListeners.handleError (dart:async/future_impl.dart:779:47)
E/flutter ( 8367): #4      Future._propagateToListeners (dart:async/future_impl.dart:800:13)
E/flutter ( 8367): #5      Future._completeError (dart:async/future_impl.dart:575:5)
E/flutter ( 8367): #6      _SyncCompleter._completeError (dart:async/future_impl.dart:51:12)
E/flutter ( 8367): #7      _Completer.completeError (dart:async/future_impl.dart:23:5)
E/flutter ( 8367): #8      Future.any.onError (dart:async/future.dart:617:45)
E/flutter ( 8367): #9      _RootZone.runBinary (dart:async/zone.dart:1665:54)
E/flutter ( 8367): #10     _FutureListener.handleError (dart:async/future_impl.dart:162:22)
E/flutter ( 8367): #11     Future._propagateToListeners.handleError (dart:async/future_impl.dart:779:47)
E/flutter ( 8367): #12     Future._propagateToListeners (dart:async/future_impl.dart:800:13)
E/flutter ( 8367): #13     Future._completeError (dart:async/future_impl.dart:575:5)
E/flutter ( 8367): #14     Future._asyncCompleteError.<anonymous closure> (dart:async/future_impl.dart:666:7)
E/flutter ( 8367): #15     _microtaskLoop (dart:async/schedule_microtask.dart:40:21)
E/flutter ( 8367): #16     _startMicrotaskLoop (dart:async/schedule_microtask.dart:49:5)


I/flutter ( 8367): ╔ DioErrorType.badResponse
I/flutter ( 8367): ║    {
I/flutter ( 8367): ║         statusCode: 415,
I/flutter ( 8367): ║         message: "File type is not matching",
I/flutter ( 8367): ║         error: "Unsupported Media Type"
I/flutter ( 8367): ║    }
I/flutter ( 8367): 

I also tried chaging the contentType to "image/*" but it throws error on the MediaType.parser (from the generated file) saying that it's an invalid type.

Is there anything I'm missing in this scenario? Have anyone solved this problem before? I tried solutions mentioned by [1],[2] and many others but still stuck in this problem. Thx in advance.

One thing to note: This request works normally in Postman with the same file.


Solution

  • I've found the answer to my problem. Someone has raised this issue in github, and apparently the root cause problem is Dio set the MediaType to application/octet-stream when we don't specify any.

    And if we specify it from retrofit like this (through contentType of the @Part),

      @POST("apiUrl/upload/files")
      @MultiPart()
      Future<TSSuccessResponse<UploadFileRemoteResponse>> uploadFile(
          @Part(contentType: "image/jpg") File file);
    

    the following line in the generated files from Retrofit will still throw an Error since it doesn't match the pattern.

    MediaType.parse(contentType)
    

    My solution is to use the normal Dio request then specify the mime type using mime package from pub.dev as per suggested from the preceeding github links.

        import 'package:mime/mime.dart';
    
        FormData data = FormData.fromMap({
          "file": await MultipartFile.fromFile(file.path,
              filename: fileName,
              contentType: MediaType.parse("${lookupMimeType(fileName)}")),
        });
    
        final response = await dio.post(
          "api/uploadFile",
          data: data,
          options: Options(headers: {"Content-Type": "multipart/form-data"}),
        );