Search code examples
javaandroidretrofitretrofit2

Retrofit 2 image upload success but image uploads as corrupted file


So heres the thing, the file is successfully uploaded as a "photo.png" but when i open the photo from the browser the photo is corrupted. When i download the photo to open it locally in my computer, the photo is corrupted.

No idea why.

Heres the retrofit endpoint:

@Multipart
@POST("alerts/{alertId}/photo/")
Call<Object> uploadPhotoStill(@Header("Authorization") String credentials, @Path("alertId") int alertId,
                              @Part("photo\"; filename=\"picture_taken.jpeg\" ") RequestBody photo);

Here is how i am using it: i am grabbing the byte array that comes back from taking a picture with the camera object.

private void initCamera() {
    if (camera == null) {
        camera = Camera.open(Camera.CameraInfo.CAMERA_FACING_FRONT);

        Camera.Parameters params = camera.getParameters();
        params.setPictureFormat(ImageFormat.JPEG);

        camera.setParameters(params);
    }
}

/**
 * Called when image data is available after a picture is taken.
 * The format of the data depends on the context of the callback
 * and {@link Camera.Parameters} settings.
 *
 * @param data   a byte array of the picture data
 * @param camera the Camera service object
 */
@Override
public void onPictureTaken(byte[] data, Camera camera) {
    Log.d(TAG, "onPictureTaken() called with: " + "data = [" + Arrays.toString(data) + "], camera = [" + camera + "]");
    handleTakenStill(data);
}

private void handleTakenStill(byte[] data) {
    Log.d(TAG, "handleTakenStill() was called");

    RequestBody requestBody = RequestBody.create(MediaType.parse("image/jpeg"), data);

    havenApi.uploadPhotoStill(Utils.encodeUserCredentials(), getCurrentAlert().getId(), requestBody).enqueue(new Callback<Object>() {
        @Override
        public void onResponse(Response<Object> response, Retrofit retrofit) {
            if (response.isSuccess()) {
                Log.d(TAG, "handleTakenStill.onResponse success: " + response.body().toString());
            } else {
                Log.e(TAG, "handleTakenStill.onResponse error: " + response.message());
            }
        }

        @Override
        public void onFailure(Throwable t) {
            Utils.logOnFailureRequest(TAG, t);
        }
    });
}

The success is always called after uploading the photo. But the photo always uploads corrupted and i have no idea why.


Solution

  • I never got to get this working with retrofit. But i was able to do it with OkHttp.

    If you are wondering how here is the solution:

    /**
     * Called when image data is available after a picture is taken.
     * The format of the data depends on the context of the callback
     * and {@link Camera.Parameters} settings.
     *
     * @param data   a byte array of the picture data
     * @param camera the Camera service object
     */
    @Override
    public void onPictureTaken(byte[] data, Camera camera) {
        Log.d(TAG, "onPictureTaken() called with: " + "data = [" + Arrays.toString(data) + "], camera = [" + camera + "]");
        handleTakenStill(data);
    }
    
    private void handleTakenStill(byte[] data) {
        Log.d(TAG, "handleTakenStill() was called");
    
        Observable.create(uploadPhoto(data))
                .subscribeOn(Schedulers.io())
                .observeOn(AndroidSchedulers.mainThread())
                .subscribe(new Subscriber<com.squareup.okhttp.Response>() {
                    @Override
                    public void onCompleted() {
                        Log.d(TAG, "onCompleted() was called");
                    }
    
                    @Override
                    public void onError(Throwable e) {
                        Utils.logOnFailureRequest(TAG, e);
                    }
    
                    @Override
                    public void onNext(com.squareup.okhttp.Response response) {
                        if (response.isSuccessful()) {
                            Log.d(TAG, "handleTakenStill.onResponse success: " + response.body().toString());
                        } else {
                            Log.e(TAG, "handleTakenStill.onResponse error: " + response.message());
                        }
                    }
                });
    }
    
    @NonNull
    private Observable.OnSubscribe<com.squareup.okhttp.Response> uploadPhoto(final byte[] data) {
        return new Observable.OnSubscribe<com.squareup.okhttp.Response>() {
            @Override
            public void call(Subscriber<? super com.squareup.okhttp.Response> subscriber) {
                OkHttpClient client = new OkHttpClient();
    
                RequestBody requestBody = new MultipartBuilder().type(MultipartBuilder.FORM)
                        .addFormDataPart("camera", "picture_taken.jpg", RequestBody.create(MediaType.parse("image/jpeg"), data))
                        .build();
    
                Request request = new Request.Builder()
                        .url(url)
                        .post(requestBody)
                        .addHeader("content-type", "multipart/form-data")
                        .addHeader("authorization", Utils.encodeUserCredentials())
                        .addHeader("accept", "application/json")
                        .addHeader("cache-control", "no-cache")
                        .build();
    
                try {
                    com.squareup.okhttp.Response response = client.newCall(request).execute();
                    subscriber.onNext(response);
                    subscriber.onCompleted();
                    if (!response.isSuccessful()) {
                        subscriber.onError(new Exception("Error uploading photo"));
                    }
                } catch (IOException e) {
                    subscriber.onError(e);
                }
            }
        };
    }