4

In my kotlin project, I use retrofit and it works well.

suspend fun createPlan(
    context: Context?,
    name: String,
    file: File?
): ABC? {

    val fileSignImage = file?.let {
        MultipartBody.Part.createFormData(
            "image",
            it.getName(),
            RequestBody.create("image/*".toMediaTypeOrNull(), it)
        )
    }

    return RetrofitFactory.apiCall(context) {
        RetrofitFactory.makeRetrofitService().createPlan(
            name.toRequestBody("text/plain".toMediaTypeOrNull()),
            fileSignImage
        )
    }} 

RetrofitService

@Multipart
@POST("create_plan")
fun createPlan(
    @Part("name") name: RequestBody,
    @Part image: MultipartBody.Part?
): Deferred<Response<WebApiResponse.ABCs>>

If I want to use Chopper, what is the correct way?

This is what I have tried

Future<Response> createPlan(
      BuildContext context, String name,String path) async {
    Response response;
    try {
      response = await _service.createPlan(
           name,path);
      return response;
    } catch (e) {
      rethrow;
    }
  }

Service

@Post(path: "create_plan")
@multipart
Future<Response> createPlan(
@Field('name') String name,@PartFile('image') String imagePath);

How can I convert the imagePath to file so I can pass it as file to server using Chopper?

Anyone?

Hoo
  • 1,806
  • 7
  • 33
  • 66
  • You have shown code snippets, but you have not mentioned what the problem is, so nobody can answer. Have you tried running that code? Are you getting any exceptions? Is the request not being sent? Is the server not receiving the request? Does the request not contain what you expect? What is the problem??? – Ovidiu Feb 02 '20 at 11:14
  • @Ovidiu I want pass File to server instead of String. – Hoo Feb 03 '20 at 02:56
  • Is your server receiving a String, or are you just ASSUMING that you are sending a String? Yet again you are not describing what is the PROBLEM (at least not with any technical proof, eg. an Exception or a strange request body) when trying to run that code snippet. You are using one of the supported data types: https://github.com/lejard-h/chopper/blob/master/chopper/lib/src/annotations.dart#L263-L280 What is not working??? – Ovidiu Feb 03 '20 at 09:32
  • @Ovidiu Server side **should not** accept `String`. In my kotlin code, I actually pass `MultipartBody.Part` to server, everything works fine. But in flutter, it failed. I can't tell you what are the erros as the error is returned from the server side. I guess is because I was passing it as `String`, not `MultipartBody.Part`? – Hoo Feb 03 '20 at 17:13
  • What do you mean you can't tell what the error is because it's returned by the server? And how do you expect anyone to help you without specifying the error? – Ovidiu Feb 04 '20 at 09:31
  • @Ovidiu The error not in my side, is on server there. On my side no error. The server expect `File`, but I was sending `String`. – Hoo Feb 04 '20 at 14:27
  • 1. Technically speaking, every request has a response, whether it's a timeout, an empty body, a specific JSON, or an HTML page. So which one is it? 2. This is the kind of problem where you need to coordinate with whoever has access to the server in order to get logs/exceptions from the server side. Without those, all you have is **assumptions**, which are just not good enough for bug fixing. The next best thing you can do place a lot of breakpoints in Chopper's code and figure out exactly what the body of the request looks like. – Ovidiu Feb 05 '20 at 09:03
  • Looking at the documentation for Chopper, the `PartFile` annotation should be exactly what you want. It's not very realistic to expect a different answer at this stage without showing us what the error message actually is (as opposed to your cryptic interpretations of it). – Abion47 Feb 05 '20 at 09:13

2 Answers2

3

Looking at the documentation for Chopper, the PartFile annotation supports three data types:

  • List<int>
  • String (path of your file)
  • MultipartFile (from package:http)

You are currently using String, but for reasons unknown it is not working for you. The first option would probably be the most straightforward, but the third option would be the most similar to what you currently have in Retrofit, so we could try that.

import 'package:http/http.dart';

...

Future<Response> createPlan(BuildContext context, String name, String path) async {
  Response response;
  try {
    final bytes = (await File(path).readAsBytes()).toList();
    final file = MultipartFile.fromBytes('image', bytes);
    response = await _service.createPlan(
      name,
      file,
    );
    return response;
  } catch (e) {
    rethrow;
  }
}

Service

@Post(path: "create_plan")
@multipart
Future<Response> createPlan(
  @Field('name') String name,
  @PartFile('image') MultipartFile image,
);
Abion47
  • 22,211
  • 4
  • 65
  • 88
  • `The method 'readAsBytes' isn't defined for the class 'File'. Try correcting the name to the name of an existing method, or defining a method named 'readAsBytes'.` – Hoo Feb 07 '20 at 04:21
  • @Hoo Did you import `dart:io`? – Abion47 Feb 07 '20 at 06:23
  • OK, I get this now after import. `Too many positional arguments: 0 expected, but 1 found. Try removing the extra arguments.` – Hoo Feb 07 '20 at 06:48
  • @Hoo Ah, I spend too much time in C# and JS these days and I keep forgetting that Dart's `File` methods aren't static. See the edit on that line. – Abion47 Feb 07 '20 at 16:30
  • still not working. Server side want to have `MultipartBody.Part` instead of `MultipartFile`. – Hoo Feb 07 '20 at 17:02
  • @Hoo That doesn't make sense, as `MultipartBody.Part` is a Java class and `MultipartFile` is a Dart class, and as far as the server is concerned, it doesn't know about or care about what the classes are, it just cares about the data. It feels like we are back to where we were in the comment thread, where this is going to be impossible for us to answer without knowing more about what errors the server is throwing and what data it is actually expecting. – Abion47 Feb 07 '20 at 17:12
  • @Hoo And by "knowing more about what errors the server is throwing", don't try to describe to us what the errors are or provide your interpretation on what they mean. Tell us word-for-word what the error message actually is, copy-and-pasted into an edit in your question with no modifications or redactions (unless there's a sensitive URL or something, censor that). – Abion47 Feb 07 '20 at 17:17
  • Hey I appreciate your answer. I was managed to fix it using `http`, but still failed using your code. I think I have to ask my colleague to get the error message since he works at backend side. – Hoo Feb 07 '20 at 17:35
  • @Hoo If it works using `http` using effectively the same method, then this may be a bug in Chopper. I'd recommend getting the error message and posting an issue on the Chopper github page. – Abion47 Feb 07 '20 at 18:52
2

I was managed to upload file using http instead of Chopper.

   Future<http.Response> createPlan(String name, String path) async {
        var request = http.MultipartRequest(
            "POST",
            Uri.parse(
                "http://xxx"));

        request.fields['name'] = name;
        request.files.add(await http.MultipartFile.fromPath(
          'image',
          path,
        ));

        try {
          var streamedResponse = await request.send();
          var response = http.Response.fromStream(streamedResponse);
          return response;
        } catch (e) {
         rethrow;
        }
      }
Hoo
  • 1,806
  • 7
  • 33
  • 66