4

I'm trying to use Robospice with Retrofit to upload an image to my Google appengine blobstore. I can get the upload URL provided by GAE, but when I try to send the URL with the image as a Multipart POST I get an exception:

E//RequestRunner.java:134(24689): Thread-3363 An exception occurred during request network execution:null

E//RequestRunner.java:134(24689): retrofit.RetrofitError

E//RequestRunner.java:134(24689): at retrofit.RestAdapter$RestHandler.invokeRequest(RestAdapter.java:343)

E//RequestRunner.java:134(24689): at retrofit.RestAdapter$RestHandler.invoke(RestAdapter.java:222)

E//RequestRunner.java:134(24689): at $Proxy0.uploadImage(Native Method)

E//RequestRunner.java:134(24689): at ginger.connexus.network.UploadImage.loadDataFromNetwork(UploadImage.java:24)

E//RequestRunner.java:134(24689): at ginger.connexus.network.UploadImage.loadDataFromNetwork(UploadImage.java:1)

E//RequestRunner.java:134(24689): at com.octo.android.robospice.request.CachedSpiceRequest.loadDataFromNetwork(CachedSpiceRequest.java:45)

E//RequestRunner.java:134(24689): at com.octo.android.robospice.request.RequestRunner.processRequest(RequestRunner.java:130)

E//RequestRunner.java:134(24689): at com.octo.android.robospice.request.RequestRunner$1.run(RequestRunner.java:197)

E//RequestRunner.java:134(24689): at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:390)

E//RequestRunner.java:134(24689): at java.util.concurrent.FutureTask.run(FutureTask.java:234)

E//RequestRunner.java:134(24689): at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1080)

E//RequestRunner.java:134(24689): at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:573)

E//RequestRunner.java:134(24689): at java.lang.Thread.run(Thread.java:841)

D//RequestProgressManager.java:75(24689): Sending progress COMPLETE

My API interface

public interface MyApi {
    @Multipart
    @POST("/{uploadurl}")
    MyImage uploadImage(
            @Path("uploadurl") String uploadurl,
            @Part("stream") long streamId,
            @Part("image") TypedFile image);
}

My Retrofit Gson Spice Service

public class MyService extends RetrofitGsonSpiceService {

    private final static String BASE_URL = "theinternet";

    @Override
    protected String getServerUrl() {
        return BASE_URL;
    }

    @Override
    public void onCreate() {
        super.onCreate();
        addRetrofitInterface(MyApi.class);
    }

}

My Retrofit Spice Request class

public class UploadImage extends RetrofitSpiceRequest<MyImage, MyApi> {

    private final String uploadurl;
    private final long streamId;
    private final TypedFile image;

    public UploadImage(String uploadurl, long streamId, File image) {
            super(MyImage.class, MyApi.class);
            this.uploadurl = uploadurl;
            this.streamId = streamId;
            this.image = new TypedFile("image/jpeg", image);
    }

    @Override
    public MyImage loadDataFromNetwork() throws Exception {
        return getService().uploadImage(uploadurl, streamId, image);
    }
}

My execute call

UploadImage upload = new UploadImage(uploadUrl, streamId, file);
getSpiceManager().execute(upload, new MyImageUploadListener());
zachwhaley
  • 107
  • 2
  • 8
  • can you upload an image with a restclient from your browser? – Aegis Oct 10 '13 at 15:06
  • I can use curl to upload the image like so: `curl -F "stream=5629499534213120" -F "image=@/path/to/image.jpg" upload-url` – zachwhaley Oct 10 '13 at 15:13
  • Your stack trace is not detailled enough. There should be a cause to this exception where you can clearly see what is going wrong. Chances are that this error comes from your Retrofit usage. – Snicolas Oct 11 '13 at 09:01
  • I think I've found something. It seems like Retrofit is changing the "/" in my upload URL to "%2F" How do I keep that from happening? – zachwhaley Oct 11 '13 at 12:57
  • Did you figure out the issue? I am also using retrofit for upload picture on my blobstore but my path_success is nerver call. I don't understand why... If you have any ideas please help me. – FlavienBert Mar 16 '14 at 09:07
  • The answer below is the proper way to do this; using `@EncodedPath`. Unfortunately I couldn't the kinks to work in time, but I did get something to work. My code is here, [connexus-android](https://github.com/zachwhaley/connexus-android). Feel free to peruse. – zachwhaley Mar 16 '14 at 15:49

1 Answers1

9

Try to use @EncodedPath instead of @Path and do URL encoding yourself.

public interface MyApi {

    @Multipart
    @POST("/{uploadurl}")
    MyImage uploadImage(@EncodedPath("uploadurl") String uploadUrlEncoded,
            ... );
}

To deal with "%2F" issue i would use next snippet

String encodedValue = URLEncoder.encode(String.valueOf(uploadUrl), "UTF-8");
// URLEncoder encodes for use as a query parameter. Path encoding uses %20 to
// encode spaces rather than +. Query encoding difference specified in HTML spec.
// Any remaining plus signs represent spaces as already URLEncoded.
encodedValue = encodedValue.replace("+", "%20");
String uploadUrlEncoded = encodedValue.replace("%2F", "/");
Sergii Pechenizkyi
  • 22,227
  • 7
  • 60
  • 71