Search code examples
androidheaderretrofitretrofit2multipart

how can I pass header using @Part in retrofit?


My interface is like this

    @Multipart
    @NonNull
    @FormUrlEncoded
    @POST("upload")
    Call<GeneralResponse> uploadImage(@Header("Authorization") MultipartBody.Part token, @Part("image") MultipartBody.Part image, @Part("kilometer") MultipartBody.Part distance);

method for uploading image and data

public void upload(final String token,final String distance, final File image) {

    InternetConnection internetConnection = new InternetConnection(TimeCard.this);
    if (internetConnection.isConnectingToInternet()) {
        showProgressDialog();
        ApiInterface apiService = ApiClient.getClient().create(ApiInterface.class);

        MultipartBody.Part bodykm;
        RequestBody kilometer;
        kilometer= RequestBody.create(MediaType.parse("text/plain"), distance);
        bodykm =MultipartBody.Part.createFormData("kilometer", distance, kilometer);


       Call<GeneralResponse> call = apiService.uploadImage(token,bodyImage,bodykm);
        call.enqueue(new Callback<GeneralResponse>() {
            @Override
            public void onResponse(Call<GeneralResponse> call, final Response<GeneralResponse> response) {

                try {
                    if(response.body().getStatus()){

                        meterImage = "";
                        distanceKm = "";
                        Toast.makeText(TimeCard.this,response.body().getMessage(),Toast.LENGTH_LONG).show();
                    }else {
                        Toast.makeText(TimeCard.this,response.body().getMessage(),Toast.LENGTH_LONG).show();
                    }
                }catch (Exception e){
                    progressDialog.dismiss();
                    e.printStackTrace();
                    Toast.makeText(TimeCard.this,e.getMessage(),Toast.LENGTH_LONG).show();
                }
            }

            @Override
            public void onFailure(Call<GeneralResponse> call, Throwable t) {
                progressDialog.dismiss();
                Toast.makeText(TimeCard.this,"Server error please try again later",Toast.LENGTH_LONG).show();
            }
        });
    } else {
        Toast.makeText(TimeCard.this,"Please check your internet connection",Toast.LENGTH_LONG).show();
    }
}

it gives error because only one type of annotation is allowed.so i change @Header to @Part

@Multipart
    @NonNull
    @FormUrlEncoded
    @POST("upload")
    Call<GeneralResponse> uploadImage(@Part("Authorization") MultipartBody.Part token, @Part("image") MultipartBody.Part image, @Part("kilometer") MultipartBody.Part distance);

now how can I pass header? (how to convert header to MultipartBody.Part?)


Solution

  • I'll start with answering your question

    it gives error because only one type of annotation is allowed

    Yes, it's intended behavior of Retrofit. You can not use multiple annotations at once. So, just remove @FormUrlEncoded annotation from uploadImage() method.

    Now, you want post distance along with the image. For that you should use like below inside uploadImage() method (in service class).

    @Part("kilometer") RequestBody distance   // note: use RequestBody instead of MultipartBody.Part
    

    And inside upload() function (in your Activity) do the changes.

    RequestBody kilometer = RequestBody.create(MediaType.parse("text/plain"), distance);
    

    Now come to the question

    how can I pass header using @Part in retrofit?

    I assume that you've dynamic authorization key (by looking into the sample code in question).

    If you have dynamic Authorization key to be set to header at runtime, you could use Retrofit's @HeaderMap annotation.

    Just do the below changes in uploadImage() method.

    @HeaderMap Map<String, String> token
    

    Inside Activity prepare header maps like below.

    Map<String, String> headers = new HashMap<>();
    headers.put("Authorization", token);
    

    If you want set other header parameters, set it in the same headers hash map.

    Then pass this header map to your uploadImage() method.

    So, the final changes in your code goes like this.

    Your service interface

    @Multipart
    @NonNull
    @POST("upload")
    Call<GeneralResponse> uploadImage(@HeaderMap Map<String, String> token, @Part("image") MultipartBody.Part image, @Part("kilometer") RequestBody distance);
    

    and in Activity

    public void upload(final String token,final String distance, final File image) {
    
    InternetConnection internetConnection = new InternetConnection(TimeCard.this);
    if (internetConnection.isConnectingToInternet()) {
        showProgressDialog();
        ApiInterface apiService = ApiClient.getClient().create(ApiInterface.class);
    
        // prepare distance body
        RequestBody kilometer = RequestBody.create(MediaType.parse("text/plain"), distance);
    
        // prepare headers
        Map<String, String> headers = new HashMap<>();
        headers.put("Authorization", token);
    
       Call<GeneralResponse> call = apiService.uploadImage(headers, bodyImage, kilometer);
        call.enqueue(new Callback<GeneralResponse>() {
            @Override
            public void onResponse(Call<GeneralResponse> call, final Response<GeneralResponse> response) {
    
                try {
                    if(response.body().getStatus()){
    
                        meterImage = "";
                        distanceKm = "";
                        Toast.makeText(TimeCard.this,response.body().getMessage(),Toast.LENGTH_LONG).show();
                    }else {
                        Toast.makeText(TimeCard.this,response.body().getMessage(),Toast.LENGTH_LONG).show();
                    }
                }catch (Exception e){
                    progressDialog.dismiss();
                    e.printStackTrace();
                    Toast.makeText(TimeCard.this,e.getMessage(),Toast.LENGTH_LONG).show();
                }
            }
    
            @Override
            public void onFailure(Call<GeneralResponse> call, Throwable t) {
                progressDialog.dismiss();
                Toast.makeText(TimeCard.this,"Server error please try again later",Toast.LENGTH_LONG).show();
            }
        });
    } else {
        Toast.makeText(TimeCard.this,"Please check your internet connection",Toast.LENGTH_LONG).show();
    }
    }
    

    Extra

    If you've static token (token that never changes per user or API key), you don't need to use header map pattern here. Just skip @HeaderMap annotation/parameter. Make the changes like below.

    @Headers("Authorization: your_token_key_here")
    @Multipart
    @NonNull
    @POST("upload")
    Call<GeneralResponse> uploadImage(@Part("image") MultipartBody.Part image, @Part("kilometer") RequestBody distance);
    

    Again if you've more than one headers, enclose header parameters inside {} braces.

    @Headers({
               "Authorization: your_token_key_here",
               "Content-Type: application/json", //  just an example
               "some other header here"
             })
    @Multipart
    @NonNull
    @POST("upload")
    Call<GeneralResponse> uploadImage(@Part("image") MultipartBody.Part image, @Part("kilometer") RequestBody distance);