Загрузить бинарный файл с охтпп из ресурсов

Мне нужно загрузить бинарный файл в apk на сервер, используя okhttp. Используя urlconnection, вы можете просто получить входной поток для актива и затем добавить его в свой запрос. Тем не менее, okhttp дает вам возможность загружать байтовые массивы, строки или файлы. Поскольку вы не можете получить путь к файлу для актива, связанного в apk, единственный вариант - скопировать файл в локальный каталог файлов (я бы предпочел этого не делать) и затем передать файл в okhttp? Нет ли способа просто сделать запрос, используя assetinputstream прямо на веб-сервере?

РЕДАКТИРОВАТЬ: я использовал принятый ответ, но вместо создания статического служебного класса я просто подклассифицировал RequestBody

 public class InputStreamRequestBody extends RequestBody {

private InputStream inputStream;
private MediaType mediaType;

public static RequestBody create(final MediaType mediaType, final InputStream inputStream) {


    return new InputStreamRequestBody(inputStream, mediaType);
}

private InputStreamRequestBody(InputStream inputStream, MediaType mediaType) {
    this.inputStream = inputStream;
    this.mediaType = mediaType;
}

@Override
public MediaType contentType() {
    return mediaType;
}

@Override
public long contentLength() {
    try {
        return inputStream.available();
    } catch (IOException e) {
        return 0;
    }
}

@Override
public void writeTo(BufferedSink sink) throws IOException {
    Source source = null;
    try {
        source = Okio.source(inputStream);
        sink.writeAll(source);
    } finally {
        Util.closeQuietly(source);
    }
 }
}

Моя единственная проблема с этим подходом - ненадежность inputtream.available() для длины содержимого. Статический конструктор должен соответствовать внутренней реализации okhttp

1 ответ

Решение

Возможно, вы не сможете сделать это напрямую с помощью библиотеки, но вы можете создать небольшой служебный класс, который сделает это за вас. Затем вы можете просто использовать его везде, где вам это нужно.

public class RequestBodyUtil {

    public static RequestBody create(final MediaType mediaType, final InputStream inputStream) {
        return new RequestBody() {
            @Override
            public MediaType contentType() {
                return mediaType;
            }

            @Override
            public long contentLength() {
                try {
                    return inputStream.available();
                } catch (IOException e) {
                    return 0;
                }
            }

            @Override
            public void writeTo(BufferedSink sink) throws IOException {
                Source source = null;
                try {
                    source = Okio.source(inputStream);
                    sink.writeAll(source);
                } finally {
                    Util.closeQuietly(source);
                }
            }
        };
    }
}

Тогда просто используйте это так

OkHttpClient client = new OkHttpClient();

MediaType MEDIA_TYPE_MARKDOWN
        = MediaType.parse("text/x-markdown; charset=utf-8");

InputStream inputStream = getAssets().open("README.md");

RequestBody requestBody = RequestBodyUtil.create(MEDIA_TYPE_MARKDOWN, inputStream);
Request request = new Request.Builder()
        .url("https://api.github.com/markdown/raw")
        .post(requestBody)
        .build();

Response response = client.newCall(request).execute();
if (!response.isSuccessful())
    throw new IOException("Unexpected code " + response);

Log.d("POST", response.body().string());    

Этот пример кода был основан на этом коде. Заменить Assets имя файла и MediaType с вашим собственным.

Я знаю, что уже поздно, но лучше, чем никогда.

Уловка здесь, если вы не знаете размер ваших данных, вы можете пропустить заголовок длины содержимого и заменить его кодировкой передачи Http1.1: Chuncked

Для получения дополнительной информации прочтите https://www.oracle.com/technical-resources/articles/javame/chunking.html.

Слишком просто использовать snoopy api: одна строчка кода, если исключить определение идентификаторов:)

URI uri = ...;
Path fileToUpload = ...;
Snoopy.builder()
      .config(SnoopyConfig.defaults())
      .build()
      .post(uri)
      .followRedirects(true)
      .failIfNotSuccessfulResponse(true)
      .body(fileToUpload)
      .consumeAsString();

https://bitbucket.org/abuwandi/snoopy

До сих пор нет релиза для Android, но он скоро появится...

Другие вопросы по тегам