How to upload an image file in Retrofit 2
Asked Answered
H

8

99

I have an image of postman like below. How can I do the same thing in Retrofit 2?

Enter image description here

I've declared the interface like this:

@Multipart
@POST("/api/Pharmarcy/UploadImage")
Call<ResponseBody> uploadPrescriptionImage(
        @Query("accessToken") String token,
        @Query("pharmarcyRequestId") int pharmacyRequestedId,
        @Part MultipartBody.Part image);
Haemostasis answered 10/10, 2016 at 7:44 Comment(1)
So what is the issue ?Iroquoian
S
205
@Multipart
@POST("user/updateprofile")
Observable<ResponseBody> updateProfile(@Part("user_id") RequestBody id,
                                       @Part("full_name") RequestBody fullName,
                                       @Part MultipartBody.Part image,
                                       @Part("other") RequestBody other);

//pass it like this
File file = new File("/storage/emulated/0/Download/Corrections 6.jpg");
RequestBody requestFile =
        RequestBody.create(MultipartBody.FORM, file);

// MultipartBody.Part is used to send also the actual file name
MultipartBody.Part body =
        MultipartBody.Part.createFormData("image", file.getName(), requestFile);

// add another part within the multipart request
RequestBody fullName = 
        RequestBody.create(MultipartBody.FORM, "Your Name");

service.updateProfile(id, fullName, body, other);

Look at the way I am passing the multipart and string params. Hope this will help you!

Salian answered 10/10, 2016 at 7:51 Comment(14)
Can you please explain what is the "user_id", "full_name" and "other"? Is it possible to update your answer based on the question params?Dordrecht
@Dordrecht These are the API request parameters.Salian
That day I had a problem (which in the end wasnt related to this thread) and I was looking at the postman photo and the only parameter in body is image, meanwhile you have also "RequestBody fullName" I thought that maybe there was smth I wasnt aware of. Thanks anyway for your replay:)Dordrecht
@Salian I just have to pass an image to the api using multipart,how to do that ?Gauge
@Gauge you can still refer to above code and pass only multi-part argument!Salian
@android_griezmann, can you look into this problem as well? I have used your code, but i am not getting images and other passed values. #53343832Untitled
@android_griezmann, is it possible to send multiparts, by generating one POJO class? I mean sending all variables by creatting one POJO?Untitled
@AviPatel I have not familiar with passing the POJO class, but I guess if request's content-type is "Application/JSON" than you can pass the POJO.Salian
i got this error /storage/emulated/0/CinHindi.pdf: open failed: ENOENT (No such file or directory)\Valuate
how to handle this request in server sideDipterocarpaceous
why creating RequestBody for String??? @Part("description") String description, @Part(value = "image", encoding = "8-bit") RequestBody image);Branton
@Salian May I know why "Your Name" is required to be sent as RequestBody?Hyp
Great answer. Also a note: When using @Part for something other than MultiBody.Part, it will NOT work. Example: This will not work - Call<ResponseBody> uploadImage(@Part UploadBody body, @Part MultipartBody.Part image);. You need the parameters Call<ResponseBody> uploadImage(@Part("user_data") UploadBody body, @Part MultipartBody.Part image); for it to work.Ninety
service.updateProfile(id, fullName, body, other); this line is giving errorUdell
I
22

For those with an inputStream, you can upload inputStream using Multipart.

@Multipart
@POST("pictures")
suspend fun uploadPicture(
    @Part part: MultipartBody.Part
): NetworkPicture

Then in perhaps your repository class:

suspend fun upload(inputStream: InputStream) {
    val part = MultipartBody.Part.createFormData(
        "pic", 
        "myPic", 
        RequestBody.create(
            MediaType.parse("image/*"),
            inputStream.readBytes()
        )
    )
    uploadPicture(part)
}

If your backend does not allow multipart, you can convert the input stream into bytes and send the byte array as the request body, like so.

// In your service
@PUT
suspend fun s3Upload(
    @Header("Content-Type") mime: String,
    @Url uploadUrl: String, 
    @Body body: RequestBody 
)

// In your repository
val body = RequestBody.create(
    MediaType.parse("application/octet"), 
    inputStream.readBytes()
)
networkService.s3Upload(mime, url, body)

To get an input stream you can do something like so.

In your fragment or activity, you need to create an image picker that returns an InputStream. The advantage of an InputStream is that it can be used for files on the cloud like google drive and dropbox.

Call pickImagesLauncher.launch("image/*") from a View.OnClickListener or onOptionsItemSelected. (See Activity Result APIs).

private val pickImagesLauncher = registerForActivityResult(
    ActivityResultContracts.GetContent()
) { uri ->
    uri?.let {
        val stream = contentResolver.openInputStream(it)
        itemViewModel.uploadPicture(stream)
    }
}

override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)

    btn.setOnClickListener {
        pickImagesLauncher.launch("image/*")
    }
}

Iridotomy answered 4/5, 2020 at 12:13 Comment(11)
How to create an InputStream from imageUri: UriKaddish
@Kaddish I have finished updating the answer to including creating an InputStreamIridotomy
If we perform readBytes without mentioning the size, isn't that will load the entire file content into memory, and cause OutOfMemory exception?Outflank
I suppose that could happen but I suspect only inn very rare circumstances like a phone with low RAM or a video file upload. In such cases, its the correct behaviour to crash. The application should not try to go around low resources, let it crash in such circumstancesAffable
@CheokYanCheng please see comment by GilbertSIridotomy
This APIs like onActivityResult are deprecated. It's better to use new API called ActivityResultContract and registerForActivityResult().Saavedra
@Mohsents Good point! Can you kindly provide an edit to my answerIridotomy
@Mohsents I tried registerForActivityResult in a Compose-only app and I can't recommend it. You end up with some awkward code. Stick with onActivityResult if you plan on doing a Compose-only app.Quadrangular
@Quadrangular Thanks for your suggestion, I still haven't tried this APIs in compose but i'm sure they fix this issues.Saavedra
Amazing !! this should be the accepted answer now as of Android 13 will provide only URI for image filesRescript
This will read an entire file in memory and wouldn't work for large files that exceed allocated memory to apps. You should instead copy over File.asRequestBody() into your project for using your own input stream.Togliatti
T
9

Upload Image See Here click This Linkenter image description here

import retrofit2.Retrofit;
import retrofit2.converter.gson.GsonConverterFactory;

class AppConfig {

    private static String BASE_URL = "http://mushtaq.16mb.com/";
    
    static Retrofit getRetrofit() {

        return new Retrofit.Builder()
                .baseUrl(AppConfig.BASE_URL)
                .addConverterFactory(GsonConverterFactory.create())
                .build();
    }
}

========================================================
import okhttp3.MultipartBody;
import okhttp3.RequestBody;
import retrofit2.Call;
import retrofit2.http.Multipart;
import retrofit2.http.POST;
import retrofit2.http.Part;

interface ApiConfig {
    @Multipart
    @POST("retrofit_example/upload_image.php")
    Call<ServerResponse> uploadFile(@Part MultipartBody.Part file,
                                    @Part("file") RequestBody name);

    /*@Multipart
    @POST("ImageUpload")
    Call<ServerResponseKeshav> uploadFile(@Part MultipartBody.Part file,
                                    @Part("file") RequestBody name);*/

    @Multipart
    @POST("retrofit_example/upload_multiple_files.php")
    Call<ServerResponse> uploadMulFile(@Part MultipartBody.Part file1,
                                       @Part MultipartBody.Part file2);
}
Tenrec answered 18/7, 2017 at 9:4 Comment(1)
can you please share key for request parameters Am getting this error in postman <br /> <b>Warning</b>: move_uploaded_file(): Unable to move '/tmp/phpbhv7zZ' to 'uploads/device-2018-07-24-132846.png' in <b>/home/u511106771/public_html/retrofit_example/upload_image.php</b> on line <b>12</b> <br /> {"success":false,"message":"Error while uploading"}Nitroparaffin
H
8

I totally agree with @tir38 and @android_griezmann. This would be the version in Kotlin:

interface servicesEndPoint {
@Multipart
@POST("user/updateprofile")
fun updateProfile(@Part("user_id") id:RequestBody, @Part("full_name") fullName:RequestBody, @Part image: MultipartBody.Part, @Part("other") other:RequestBody): Single<UploadPhotoResponse>

companion object {
        val API_BASE_URL = "YOUR_URL"

        fun create(): servicesPhotoEndPoint {
            val retrofit = Retrofit.Builder()
                .addCallAdapterFactory(RxJava2CallAdapterFactory.create())
                .addConverterFactory(GsonConverterFactory.create())
                .baseUrl(API_BASE_URL)
                .build()
            return retrofit.create(servicesPhotoEndPoint::class.java)
        }
    }
}

// Pass it like this
val file = File(RealPathUtils.getRealPathFromURI_API19(context, uri))
val requestFile: RequestBody = RequestBody.create(MediaType.parse("multipart/form-data"), file)

// MultipartBody.Part is used to send also the actual file name
val body: MultipartBody.Part = MultipartBody.Part.createFormData("image", file.name, requestFile)

// Add another part within the multipart request
val fullName: RequestBody = RequestBody.create(MediaType.parse("multipart/form-data"), "Your Name")

servicesEndPoint.create().updateProfile(id, fullName, body, fullName)

To obtain the real path, use RealPathUtils. Check this class in the answers of @Harsh Bhavsar in this question: How to get the Full file path from URI.

To getRealPathFromURI_API19, you need permissions of READ_EXTERNAL_STORAGE.

Hypanthium answered 2/6, 2019 at 6:5 Comment(0)
G
2

Using Retrofit 2.0 you may use this:

@Multipart
    @POST("uploadImage")
    Call<ResponseBody> uploadImage(@Part("file\"; fileName=\"myFile.png\" ")RequestBody requestBodyFile, @Part("image") RequestBody requestBodyJson);

Make a request:

File imgFile = new File("YOUR IMAGE FILE PATH");
RequestBody requestBodyFile = RequestBody.create(MediaType.parse("image/*"), imgFile);
RequestBody requestBodyJson = RequestBody.create(MediaType.parse("text/plain"),
                    retrofitClient.getJsonObject(uploadRequest));



//make sync call
Call<ResponseBody> uploadBundle = uploadImpl.uploadImage(requestBodyFile, requestBodyJson);
Response<BaseResponse> response = uploadBundle.execute();

please refer https://square.github.io/retrofit/

Geomancer answered 10/10, 2016 at 8:6 Comment(1)
how to pass dynamic name?Myrmecology
B
2
@Multipart
@POST(Config.UPLOAD_IMAGE)
Observable<Response<String>> uploadPhoto(@Header("Access-Token") String header, @Part MultipartBody.Part imageFile);

And you can call this api like this:

   public void uploadImage(File file) {
     // create multipart
     RequestBody requestFile = RequestBody.create(MediaType.parse("multipart/form-data"), file);
    MultipartBody.Part body = MultipartBody.Part.createFormData("image", file.getName(), requestFile);

    // upload
    getViewInteractor().showProfileUploadingProgress();

    Observable<Response<String>> observable = api.uploadPhoto("",body);

    // on Response
    subscribeForNetwork(observable, new ApiObserver<Response<String>>() {
        @Override
        public void onError(Throwable e) {
            getViewInteractor().hideProfileUploadingProgress();
        }

        @Override
        public void onResponse(Response<String> response) {

            if (response.code() != 200) {
                Timber.d("error " + response.code());
                return;
            }
            getViewInteractor().hideProfileUploadingProgress();
            getViewInteractor().onProfileImageUploadSuccess(response.body());

        }
    });

}
Bandage answered 10/10, 2016 at 8:8 Comment(4)
I dont understand After on response like subscribeForNetwork(... can you please Explain itInkblot
@HamzaRahman sorry it was my custom method, from rxjava2 new CompositeDisposable().add(observable .subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) .subscribeWith(new DisposableObserver<Void>() { public void onNext(Void aVoid) { } public void onError(Throwable e) { } })); }Bandage
Ok Now I Got It Thanks @BandageInkblot
happy to help youBandage
E
2

Retrofit 2.0 solution

@Multipart
@POST(APIUtils.UPDATE_PROFILE_IMAGE_URL)
public Call<CommonResponse> requestUpdateImage(@PartMap Map<String, RequestBody> map);

and

    Map<String, RequestBody> params = new HashMap<>();

    params.put("newProfilePicture" + "\"; filename=\"" + FilenameUtils.getName(file.getAbsolutePath()), RequestBody.create(MediaType.parse("image/jpg"), file));



 Call<CommonResponse> call = request.requestUpdateImage(params);

you can use
image/jpg image/png image/gif

Eudemon answered 27/6, 2019 at 10:53 Comment(1)
i am want send name email as well with image, how can i do this?Popliteal
B
1

It is quite easy. Here is the API Interface

public interface Api {

    @Multipart
    @POST("upload")
    Call<MyResponse> uploadImage(@Part("image\"; filename=\"myfile.jpg\" ") RequestBody file, @Part("desc") RequestBody desc);

}

And you can use the following code to make a call.

private void uploadFile(File file, String desc) {

        //creating request body for file
        RequestBody requestFile = RequestBody.create(MediaType.parse(getContentResolver().getType(fileUri)), file);
        RequestBody descBody = RequestBody.create(MediaType.parse("text/plain"), desc);

        //The gson builder
        Gson gson = new GsonBuilder()
                .setLenient()
                .create();


        //creating retrofit object
        Retrofit retrofit = new Retrofit.Builder()
                .baseUrl(Api.BASE_URL)
                .addConverterFactory(GsonConverterFactory.create(gson))
                .build();

        //creating our api 
        Api api = retrofit.create(Api.class);

        //creating a call and calling the upload image method 
        Call<MyResponse> call = api.uploadImage(requestFile, descBody);

        //finally performing the call 
        call.enqueue(new Callback<MyResponse>() {
            @Override
            public void onResponse(Call<MyResponse> call, Response<MyResponse> response) {
                if (!response.body().error) {
                    Toast.makeText(getApplicationContext(), "File Uploaded Successfully...", Toast.LENGTH_LONG).show();
                } else {
                    Toast.makeText(getApplicationContext(), "Some error occurred...", Toast.LENGTH_LONG).show();
                }
            }

            @Override
            public void onFailure(Call<MyResponse> call, Throwable t) {
                Toast.makeText(getApplicationContext(), t.getMessage(), Toast.LENGTH_LONG).show();
            }
        });
    }

Source: Retrofit Upload File Tutorial.

Billboard answered 5/10, 2017 at 17:27 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.