Android Http Публикация в REST Api

Привет, я в первую очередь разработчик Android и совершенно не разбираюсь в http-протоколах, ваша помощь будет очень признательна!

Я пытаюсь сделать вызов API для этого: http://api.haikutest.com/developer/apidoc/assignments.html, где я размещаю файл на устройстве.

Они дают образец запроса:

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

И в описании указано, что параметр файлов:

Массив файлов, каждый элемент которого содержит хэш имени файла и файла в кодировке base64.

Это код, который я пробую прямо сейчас (примечание: я использую Robospice + Spring для 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;

Но я получаю сообщение об ошибке, говорящее, что он не может сериализовать поток в JSON. Следуя документации по API, нужно ли размещать поток в качестве параметра? И как правильно это реализовать? Я был бы рад, если бы мне не нужно было ограничивать размер файла для пользователя, а значит, и потоки. Хотя я заставил его работать с небольшими файлами, закодированными в Base64 и помещенными непосредственно в качестве параметра...

Любая помощь приветствуется! Я определенно не буду отрицать комментарии или ответы для небольших указателей.

Обновлено

Обновлен код, но по-прежнему появляется другое сообщение об ошибке:

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]

person Ryan S    schedule 06.08.2013    source источник
comment
Я не совсем уверен, но я сомневаюсь, что вы можете передать файл, если служба ожидает, что вы просто отправите 1 полный запрос   -  person Stefan de Bruijn    schedule 06.08.2013
comment
@stefan в образце запроса есть файл слова, и он очень короткий для PDF ... Как они это сделали?   -  person Ryan S    schedule 06.08.2013


Ответы (1)


Похоже, вы используете Spring for Android. Судя по этому сообщению об ошибке, кажется, что Spring заставляет тип контента быть application/json, в то время как я полагаю, что вы пытаетесь отправить application/x-www-form-urlencoded. Итак, установите этот заголовок Content-Type:

headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED);

Тогда Spring будет знать, что нужно соответствовать правильному MessageConverter. Подробнее об этом здесь .

EDIT: Кроме того, вы должны отправлять данные запроса в данных POST, а не в URI запроса. Переместите это в MultiValuesMap. Посмотрите на ссылку, которую я дал вам, как размещать такие данные. Полную документацию по ним также можно найти здесь .

EDIT2 Вам также необходимо установить MessageConverter для своего ответа. Что-то типа:

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

EDIT3. Если вам нужно отправить файлы, рассмотрите возможность использования Resource, точнее InputStreamResource. Но есть хитрость: вам нужно указать имя файла и размер файла, иначе ваш запрос вылетит. Что-то типа:

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();
                }
            };
person gunar    schedule 06.08.2013
comment
Спасибо, дома попробую - person Ryan S; 06.08.2013
comment
Я пробовал это, но это не сработало... Я обновил свой вопрос, чтобы отразить это. Что я делаю неправильно? - person Ryan S; 06.08.2013
comment
Все дело в том, чтобы найти соответствующий MessageConverter для того, что вы отправляете. Можешь попробовать headers.setContentType(MediaType.TEXT_PLAIN);? - person gunar; 06.08.2013
comment
... и затем вам нужно установить соответствующий MessageConverter для ответа. Отредактировал мой ответ для этого. - person gunar; 06.08.2013
comment
В чем была проблема/решение? - person gunar; 07.08.2013
comment
Частичное решение находится здесь: stackoverflow.com/questions/18095020/ ключ использовал FileSystemResource - person Ryan S; 07.08.2013
comment
Да, но это расстраивает, потому что это все еще выбрасывает из памяти - person Ryan S; 07.08.2013
comment
Я использую InputStreamResource, потому что таким образом я не загружаю весь файл в память, как это делает FileSystemResource. Я обновлю свой ответ, чтобы показать вам, как я это сделал. Есть трюк с InputStreamResource. - person gunar; 07.08.2013
comment
Спасибо за помощь, обязательно дам награду, если вы решите это. Я обновил свой код (по новому вопросу), но все еще получаю outOfMemory? Как это возможно, когда он даже не загружает все это? - person Ryan S; 07.08.2013
comment
Даже с InputStreamResource вы получаете OOE? Я отправляю изображения, размер которых составляет около 4 МБ или более, а потребление памяти минимально. Вы уверены, что не загружаете весь файл в память? - person gunar; 07.08.2013
comment
Да, и странно то, что он отлично работает до 20 МБ, что означает, что он должен загружать небольшие части, но когда я пытаюсь использовать файл размером 50 МБ, он дает OOE. - person Ryan S; 07.08.2013
comment
давайте продолжим это обсуждение в чате - person Ryan S; 07.08.2013