0

Hi I'm primarily an Android developer and am pretty clueless about http protocols, you help would be much appreciated!

I am trying to make an api call to this: http://api.haikutest.com/developer/apidoc/assignments.html where I POST a file on the device.

They give a sample request of:

POST /api/assignments/3/submit
message[subject]=Subject+of+the+message&message[body]=Some+long+text&message[sent_at]=2013-07-05&message[draft]=0&message[assignment_id]=3&files[][file]=%23%3CFile%3A0x007ff59c9c1de8%3E&files[][filename]=test.pdf

And in the description state that the files param is:

Array of files with each element containing a hash of filename and base64 encoded file

This is the code that I'm trying right now (note: I'm using Robospice + Spring for Android):

   File file = new File(Environment.getExternalStorageDirectory() +
                "/DCIM/Camera/1357279228432.jpg");
        Base64InputStream stream = new Base64InputStream(new FileInputStream(file), 0);
        HttpHeaders headers = new HttpHeaders();
        headers.add("x-haiku-auth", HaikuAPI.getAuthHeader());
        headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED);
        MultiValueMap<String, Object> parts = new LinkedMultiValueMap<String, Object>();
        parts.add("files[][file]", parts);
        HttpEntity<Object> request = new HttpEntity<Object>(stream, headers);
        UriComponentsBuilder builder = UriComponentsBuilder
                .fromUriString(
                        "https://" + HaikuAPI.getDomain() + "/api/assignments")
                .pathSegment(assignmentID + "", "submit")
                .queryParam("message[subject]", "Assignment Completed")

                .queryParam("message[body]", message)
                .queryParam("message[assignment_id]", assignmentID)
                .queryParam("files[][filename]", "test.jpg");

        URI url = builder.build().toUri();

        String response = getRestTemplate().postForObject(url, request, String.class);
        return response;

But I get an error saying that it can't serialize the stream to JSON. Following the API documentation do I have to place the stream as a param? Also what is the correct way to implement this? I would love if I didn't have to put a file size limit on the user, hence the streams. Though I have gotten it to work with small files encoded in Base64 and placed directly as a param...

Any help is greatly appreciated! I will definitely not downvote comments or answers for small pointers.

Updated

Updated code, but still get another error message:

00:25:50.904 Thread-2066 An exception occurred during request network execution :Could not write request: no suitable HttpMessageConverter found for request type [org.springframework.util.LinkedMultiValueMap] and content type [application/x-www-form-urlencoded]
Ryan S
  • 4,549
  • 2
  • 21
  • 33
  • 1
    I'm not quite sure, but I doubt you can stream a file over, if the service expects you to simply send 1 complete request – Stefan de Bruijn Aug 06 '13 at 06:21
  • @stefan the sample request has the word file in it and is extremely short for a PDF... How did they do that? – Ryan S Aug 06 '13 at 06:30

1 Answers1

1

Looks like you're using Spring for Android. Judging that error message it seems Spring forces the content type to be application/json, while I believe you're trying to send application/x-www-form-urlencoded. So set this Content-Type header:

headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED);

Spring will know then to match the proper MessageConverter. More info on this here.

EDIT: Also, you're supposed to send the request data in POST data, not in request URI. Move that to a MultiValuesMap. Have a look at the link I gave you how to post such data. A complete documentation on these can also be found here.

EDIT2 You need to set the MessageConverter for your response as well. Something like:

List<MediaType> acceptableMediaTypes = new ArrayList<MediaType>();
acceptableMediaTypes.add(MediaType.APPLICATION_JSON);
headers.setAccept(acceptableMediaTypes);

EDIT3 If you need to send files, consider using Resource, to be more specific InputStreamResource. But there is a trick: you need to provide the file name and the file size, otherwise your request will crash. Something like:

Resource res = new InputStreamResource(context.getContentResolver().openInputStream(itemImage)) {
                @Override
                public String getFilename() throws IllegalStateException {
                    return fileInfo.getFileName();
                }

                @Override
                public long contentLength() throws IOException {
                    return fileInfo.getFileSizeInBytes();
                }
            };
gunar
  • 14,660
  • 7
  • 56
  • 87
  • Thank you I'll try it out when I get home – Ryan S Aug 06 '13 at 06:33
  • I tried this but it didn't work... I updated my question to reflect this. What am I doing wrong? – Ryan S Aug 06 '13 at 07:23
  • It's all a matter of finding the corresponding MessageConverter for what you're sending. Can you try `headers.setContentType(MediaType.TEXT_PLAIN);`? – gunar Aug 06 '13 at 07:35
  • ... and then you need to set the corresponding MessageConverter for response. Edited my answer for that. – gunar Aug 06 '13 at 07:37
  • What was the problem/solution? – gunar Aug 07 '13 at 06:26
  • Partial solution is here: http://stackoverflow.com/questions/18095020/android-spring-file-upload-outofmemory the key was using FileSystemResource – Ryan S Aug 07 '13 at 06:46
  • Yep, but it's frustrating because it still throws that outofmemory – Ryan S Aug 07 '13 at 07:15
  • I'm using an InputStreamResource because this way I am not loading the whole file in memory as FileSystemResource does. I'll update my answer to show you how I did it. There is a trick with `InputStreamResource`. – gunar Aug 07 '13 at 07:25
  • Thanks for being so helpful, definitely giving a bounty if you solve it. I updated my code (on the new question), but still get the outOfMemory? How is that possible, when it's not even loading the whole thing? – Ryan S Aug 07 '13 at 08:18
  • Even with `InputStreamResource` you're getting OOE? I'm sending images that have about 4MB or more and the memory consumption is minimal. Are you sure you're not loading the the whole file in memory? – gunar Aug 07 '13 at 08:19
  • Yes and the weird thing is it works up to like 20mb fine, which means it must be loading small parts, but when I try with a 50mb file it gives the OOE – Ryan S Aug 07 '13 at 08:21
  • let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/34970/discussion-between-rsenapps-and-gunar) – Ryan S Aug 07 '13 at 08:23