jsonflutterdartmultipartform-datamultipartfile

How to send a Json object and MultipartFile from Flutter to a server using MultipartRequest


I´'m trying to send a multipart request from Flutter to an API. In this multipart request there is a MultipartFile (image) and a Json object. The request works on Postman and React, so is not really a server problem. Every time I try to do the request i get a 415 error. Also the problem doesn´t seems to be from the headers.

This is the following method I´ve made in flutter:

Future<dynamic> postMultipartChetada(String url, List<XFile> files, dynamic data, String dataName) async {
    late LocalStorageService _localStorageService;
    GetIt.I
        .getAsync<LocalStorageService>()
        .then((value) => _localStorageService = value);
    final request = http.MultipartRequest(
      'POST',
      Uri.parse(ApiConstants.baseUrl + url),
      
    );
    if(files.isNotEmpty){
      for (final file in files) {
      final bytes = await file.readAsBytes();
      request.files.add(http.MultipartFile.fromBytes(
        'file',
        bytes,
        filename: file.name.substring(1, 10),
      ));
    }

    }
    request.fields[dataName] = jsonEncode(data.toJson());
    var boundary = Uuid().v4();
    final headers = {
      'Authorization': 'Bearer ${_localStorageService.getFromDisk("user_token")}',
      'Content-Type': 'multipart/form-data; boundary=$boundary',
      "Accept": "*/*",
    };
    request.headers.addAll(headers);

    try {
      final response = await request.send();
      return response;
    } catch (error) {
      throw new Exception("Error en el cliente");
    }
  }

Using the request register of SpringBoot(where the API is made) I get the following information:

2023-05-29 22:50:33.344 DEBUG 27288 --- [io-8080-exec-10] o.s.web.servlet.DispatcherServlet        : POST "/question", parameters={multipart}
2023-05-29 22:50:33.383 DEBUG 27288 --- [io-8080-exec-10] s.w.s.m.m.a.RequestMappingHandlerMapping : Mapped to com.triana.salesianos.HazTuHuertoAPI.controller.QuestionController#register(MultipartFile, CreateQuestion, User)
2023-05-29 22:50:33.386 DEBUG 27288 --- [io-8080-exec-10] o.s.web.method.HandlerMethod             : Could not resolve parameter [1] in public org.springframework.http.ResponseEntity<com.triana.salesianos.HazTuHuertoAPI.model.dto.question.QuestionDetails> com.triana.salesianos.HazTuHuertoAPI.controller.QuestionController.register(org.springframework.web.multipart.MultipartFile,com.triana.salesianos.HazTuHuertoAPI.model.dto.question.CreateQuestion,com.triana.salesianos.HazTuHuertoAPI.model.User): Content type 'application/octet-stream' not supported
2023-05-29 22:50:33.387 DEBUG 27288 --- [io-8080-exec-10] .m.m.a.ExceptionHandlerExceptionResolver : Using @ExceptionHandler com.triana.salesianos.HazTuHuertoAPI.error.GlobalRestControllerAdvice#handleException(Exception, WebRequest)
2023-05-29 22:50:33.401 DEBUG 27288 --- [io-8080-exec-10] o.s.w.s.m.m.a.HttpEntityMethodProcessor  : Using 'application/json', given [*/*] and supported [application/json, application/*+json, application/json, application/*+json]
2023-05-29 22:50:33.413 DEBUG 27288 --- [io-8080-exec-10] o.s.w.s.m.m.a.HttpEntityMethodProcessor  : Writing [ApiErrorImpl(status=415 UNSUPPORTED_MEDIA_TYPE, message=Content type 'application/octet-stream' not  (truncated)...]
2023-05-29 22:50:33.416 DEBUG 27288 --- [io-8080-exec-10] .m.m.a.ExceptionHandlerExceptionResolver : Resolved [org.springframework.web.HttpMediaTypeNotSupportedException: Content type 'application/octet-stream' not supported]
2023-05-29 22:50:33.417 DEBUG 27288 --- [io-8080-exec-10] o.s.web.servlet.DispatcherServlet        : Completed 415 UNSUPPORTED_MEDIA_TYPE

I suppose the error is that is not detecting well the Content-Type header, but i´m not sure if its the Conten-Type of the whole request or just for one of the fields and no idea of how to fix it.


Solution

  • final String _baseURL="baseurlhere";
      final http.Client _client=http.Client();
    
      Map<String,String> _headers(){
        Map<String,String> headers={
          'Content-Type':'application/json'
        };
        if(Global.isLogged){
          headers['Authorization']='Bearer ${Global.user['token']}';
        }
        return headers;
      }
    
    void postMultipart({
        required String endpoint,
        Map body=const{},
        Map<String,File> files=const{},
        required Function(Map response) onSuccess,
        required Function(Map response)onError
      })async{
        try{
          final http.MultipartRequest multipartRequest= http.MultipartRequest('POST',Uri.parse(_baseURL+endpoint));
          multipartRequest.headers.addAll(_headers());
          body.forEach((key, value) {
            multipartRequest.fields[key]=value;
          });
          files.forEach((key,file) async{
            var multipartFile= await http.MultipartFile.fromPath(key,file.path);
            multipartRequest.files.add(multipartFile);
          });
          final http.StreamedResponse streamedResponse=await multipartRequest.send();
          final http.Response response=await http.Response.fromStream(streamedResponse);
          _handleResponse(response: response,onSuccess: onSuccess, onError: onError);
        }on http.ClientException catch(e) {
          onError({"error":e.message});
        }catch(e){
          onError({"error":e.toString()});
        }
      }