8

I am using RetroFit on android in order to send images to a REST server (developed using Python and Flask). The app sends a first image to the server which stores it and that returns that image's ID to the client. The client then takes another photo and sends it to the server, this time with the previous image ID in the endpoint URL. The server should then compute the rotation from image A to image B and return the result. Once the client has sent the second image, it should repeatedly poll the endpoint for a result until the calculations are completed server side.

The first image sends correctly, and the server stores it. The client then correctly receives the response from the server. The problem occurs when the client sends the second image. The server receives the POST correctly, but the client never seems to get a response. This is also true of the third message, the GET request that polls for a result, it is received by the server but the client never sees a response.

The code for my REST server is here

class Image(Resource):
    def post(self):
        file = request.files['uploaded_file']
        centerX = request.form['center_x']
        centerY = request.form['center_y']
        if file and allowed_file(file.filename):
            filename = secure_filename(file.filename)
            file.save(os.path.join(app.config['UPLOAD_FOLDER'], str(id(True)) + ".jpg"))
            result = {'id' : id(), 'error' : True, 'result' : 5.5}
            return jsonify(result)
        else:
            return {'id' : -1, 'error' : False}

class Find_Transform(Resource):
    def post(self, first_id):
        result = {'id' : 1, 'error' : True, 'result' : 1.2}
        return jsonify(result)

class Find_Transform_Result(Resource):
    def get(self, first_id, second_id):
        print("Hello")
        result = {'id' : 0, 'error' : True, 'result' : 1.1}
        return jsonify(result)


api.add_resource(Image,'/image')
api.add_resource(Find_Transform,'/image/<first_id>/find-transform')
api.add_resource(Find_Transform_Result, '/image/<first_id>/find-transform/<second_id>')

if __name__ == '__main__':
    app.run(host='0.0.0.0',port='5002')

As you can see some of the endpoints are still just stubs to test the REST api.

My java Respond.class is here

public class Respond {

    private int id;
    private Boolean error;
    private float result;

    public int getMessage() {
        return id;
    }


    public void setMessage(int id) {
        this.id = id;
    }

    public Boolean getError() {
        return error;
    }

    public void setError(Boolean error) {
        this.error = error;
    }

    public void setResult(float result) {
        System.out.println("Got Result");
        this.result = result;
    }

    public float getResult() {
        return result;
    }
}

My Retrofit interface is here

public interface FileApi {

    @Multipart
    @POST("/image")
    Call<Respond> uploadCropImage(@Part MultipartBody.Part file, @Part MultipartBody.Part x, @Part MultipartBody.Part y);

    @Multipart
    @POST("/image/{first_id}/find-transform")
    Call<Respond> uploadCompareImage(@Path("first_id") int id, @Part MultipartBody.Part file);

    @GET("/image/{first-id}/find-transform/{second-id}")
    Call<Respond> getCompareResult(@Path("first-id") int firstId, @Path("second-id") int secondId);
}

The reason for the two separate file upload functions is that the first image needs to be sent alongside a user selected centre point, while the second is just an image.

Finally the sections where I build my requests are here:

 private void uploadFirstImage(byte[] imageBytes) {

        FileApi service = RetroClient.getApiService();


        RequestBody requestFile = RequestBody.create(MediaType.parse("multipart/form-data"), imageBytes);

        MultipartBody.Part fileBody =
                MultipartBody.Part.createFormData("uploaded_file", "first_image.jpg", requestFile);


        RequestBody image_center_x = RequestBody.create(MediaType.parse("multipart/form-data"), Float.toString(points[0].x));
        MultipartBody.Part xBody = MultipartBody.Part.createFormData("center_x", null, image_center_x);

        RequestBody image_center_y = RequestBody.create(MediaType.parse("multipart/form-data"), Float.toString(points[0].y));
        MultipartBody.Part yBody = MultipartBody.Part.createFormData("center_y", null, image_center_y);


        Call<Respond> resultCall = service.uploadCropImage(fileBody, xBody, yBody);

        resultCall.enqueue(new Callback<Respond>() {
            @Override
            public void onResponse(Call<Respond> call, Response<Respond> response) {

                // Response Success or Fail
                if (response.isSuccessful()) {
                    if (response.body().getError()==true){
                        System.out.println(Double.toString(response.body().getResult()));
                        session_id = response.body().getMessage();
                    }


                } else {
                    Toast.makeText(getApplicationContext(),"Failed to communicate with server",Toast.LENGTH_LONG).show();
                }


            }

            @Override
            public void onFailure(Call<Respond> call, Throwable t) {
            }
        });
    }

and here:

private void uploadSecondImage(byte[] imageBytes) {

        FileApi service = RetroClient.getApiService();


        RequestBody requestFile = RequestBody.create(MediaType.parse("multipart/form-data"), imageBytes);

        MultipartBody.Part fileBody =
                MultipartBody.Part.createFormData("uploaded_file", "first_image.jpg", requestFile);


        Call<Respond> resultCall = service.uploadCompareImage(session_id,fileBody);

        resultCall.enqueue(new Callback<Respond>() {
            @Override
            public void onResponse(Call<Respond> call, Response<Respond> response) {

                // Response Success or Fail
                if (response.isSuccessful()) {
                    if (response.body().getError()==true){
                        System.out.println("Got Second Response");
                        resultID = response.body().getMessage();
                    }


                } else {
                    Toast.makeText(getApplicationContext(),"Failed to communicate with server",Toast.LENGTH_LONG).show();
                }


            }

            @Override
            public void onFailure(Call<Respond> call, Throwable t) {
                System.out.println("Failure");
            }
        });
    }

and finally, for the GET request

    final boolean[] resultReady = {false};
    while (!resultReady[0]) {

        FileApi service = RetroClient.getApiService();
        Call<Respond> resultCall = service.getCompareResult(session_id, resultID);
        resultCall.enqueue(new Callback<Respond>() {
            @Override
            public void onResponse(Call<Respond> call, Response<Respond> response) {

                // Response Success or Fail
                if (response.isSuccessful()) {
                    if (response.body().getError()){
                        System.out.println("Got Response");
                        resultReady[0] = true;
                        theta[0] = response.body().getResult();
                        Toast.makeText(getApplicationContext(),"Measurement Recieved",Toast.LENGTH_LONG).show();
                    }
                    else {
                        Toast.makeText(getApplicationContext(),"Error Recieved",Toast.LENGTH_LONG).show();
                    }


                } else {
                    Toast.makeText(getApplicationContext(),"Failed to communicate with server",Toast.LENGTH_LONG).show();
                }


            }

            @Override
            public void onFailure(Call<Respond> call, Throwable t) {
            }
        });



    }

I've tried putting breakpoints in the OnResponse functions, and have put debug statements in both OnResponse and OnFailure and it seems that that code is never run. What could be going wrong here?

EDIT: Okay so I've got some logging data. I've tested the server from a python console

In [1]: import requests

In [2]: requests.get("http://localhost:5002/image/1/find-transform/2")
Out[2]: <Response [200]>

In [3]: _.text
Out[3]: '{"error": true, "id": 0, "result": 1.1}'

In [4]: requests.post("http://localhost:5002/image/find-transform")
Out[4]: <Response [404]>

In [5]: requests.post("http://localhost:5002/image/1/find-transform")
Out[5]: <Response [200]>

In [6]: _.text
Out[6]: '{"error": true, "id": 1, "result": 1.2}'

I also put the http logging in the client side as suggested by a comment, which gave this logging output.

D/OkHttp: --> POST http://192.168.0.19:5002/image
    Content-Type: multipart/form-data; boundary=bc4ae771-2762-4124-8f07-d00dc925d886
D/OkHttp: Content-Length: 2044497
D/OkHttp: --bc4ae771-2762-4124-8f07-d00dc925d886
    Content-Disposition: form-data; name="uploaded_file"; filename="first_image.jpg"
    Content-Type: multipart/form-data
    Content-Length: 2043935
    (Truncated raw image data)
V/RenderScript: 0x7f77b4c000 Launching thread(s), CPUs 8
D/OkHttp: <-- 200 OK http://192.168.0.19:5002/image (410ms)
    Content-Type: application/json
    Content-Length: 39
    Server: Werkzeug/0.14.1 Python/3.6.8
    Date: Wed, 24 Jul 2019 17:00:47 GMT
D/OkHttp: {"error": true, "id": 9, "result": 5.5}
    <-- END HTTP (39-byte body)
I/System.out: 5.5
W/ImageReader_JNI: Unable to acquire a lockedBuffer, very likely client tries to lock more than maxImages buffers
I/System.out: Resultcall
D/OkHttp: --> POST http://192.168.0.19:5002/image/9/find-transform
    Content-Type: multipart/form-data; boundary=63de36cb-0961-4802-a97f-57b98ebcd8cb
D/OkHttp: Content-Length: 1655614
D/OkHttp: --63de36cb-0961-4802-a97f-57b98ebcd8cb
    Content-Disposition: form-data; name="uploaded_file"; filename="first_image.jpg"
    Content-Type: multipart/form-data
    Content-Length: 1655386
    (Truncated raw image data)
D/OkHttp: <-- HTTP FAILED: java.net.SocketException: sendto failed: EPIPE (Broken pipe)
D/OkHttp: --> GET http://192.168.0.19:5002/image/9/find-transform/-1
    --> END GET
D/OkHttp: <-- 200 OK http://192.168.0.19:5002/image/9/find-transform/-1 (90ms)

It seems that the server is both correctly receiving the request, and sending the correct response. So something must be failing to be processed correctly client side, but I can't think what.

Cœur
  • 37,241
  • 25
  • 195
  • 267
Birdfriender
  • 343
  • 1
  • 7
  • 24
  • Just wondering but why don't you create 1 POST endpoint where 2 images are uploaded? – Zun Jul 15 '19 at 14:03
  • @Zun The photos are taken in real time and uploaded immediately, so I figured having one been sent while the user is taken the second one made sense. What would be the advantage of combining them? – Birdfriender Jul 16 '19 at 08:47
  • 5
    Include [HttpLogginInterceptor](https://stackoverflow.com/questions/32514410/logging-with-retrofit-2) in your project and examine the logs. That will guide you in the right direction. If you don't solve it, post relevant logs here and we'll try to help. – Vucko Jul 17 '19 at 15:03
  • @Vucko it seems that the error is "java.net.SocketException: sendto failed: EPIPE (Broken pipe)" which I understand to be to do with a connection being prematurely closed, but I'm not sure how that would happen with a REST server – Birdfriender Jul 19 '19 at 10:28
  • Any logs on server and client side? – Hash Jul 23 '19 at 05:39
  • @Birdfriender Did you try this request in postman? – Manoj Perumarath Jul 24 '19 at 11:13
  • @Mark I've added those now – Birdfriender Jul 24 '19 at 17:10
  • @ManojPerumarath I tried creating the requests with a python terminal, which I've added to the post now – Birdfriender Jul 24 '19 at 17:10

0 Answers0