Как приостановить/возобновить загрузку с помощью okhttp в Android

Я использую библиотеку okhttp для загрузки файлов в Android. Я успешно скачиваю. Но что-то не так, когда я приостанавливаю и возобновляю загрузку.

Response request = new Request.Builder().url(url).build();
ResponseBody responseBody = response.body();

File file = new File(filePath);
BufferedInputStream input = new BufferedInputStream(responseBody.byteStream());
OutputStream output;

if (isResume) {
    output = new FileOutputStream(file, true);
    input.skip(downloadedSize);
} else {
    output = new FileOutputStream(file, false);    
}

long totalByteSize = responseBody.contentLength();
byte[] data = new byte[1024];
int count = 0;

while ((count = input.read(data)) != -1) {
    downloadedSize += count;
    output.write(data, 0, count);   
}

Проблема в том, что, например, размер файла составляет 10 МБ. Я делаю паузу, когда он загружает 3 МБ, а затем возобновляю загрузку, а когда загрузка заканчивается, размер файла становится 13 МБ. Он не начинается с загруженного размера при возобновлении, он начинает загрузку с начала потока байтов. поэтому файл становится 13 МБ. Что не так с кодом?


person Alexander    schedule 11.04.2017    source источник
comment
Вы создаете новый запрос и отправляете его. Это приводит к тому, что ответ содержит весь файл. Я не уверен, есть ли у okHttp возможность загружать частичные ответы, но если это так, вы этого не сделаете. По крайней мере, вам нужно будет передать некоторые данные о том, откуда начать загрузку.   -  person Gabe Sechan    schedule 11.04.2017
comment
Я не вижу никаких проблем с вашим подходом, возможно, ваш размер загрузки равен 0, что, в свою очередь, ничего не пропускает. Я действительно не нахожу никакой разницы между вопросом и ответом, описанным первым способом. Если есть какая-то конкретная разница, дайте мне знать   -  person Killer    schedule 17.05.2019
comment
Отличия @Killer заключаются в source.skip(downloadedSize) . Код, который вы не видите, я получаю статус загрузки файла и загруженный размер из db. Например, размер файла составляет 5,5 МБ, а в прошлом процессе загрузки я скачал 3 МБ. Следующий процесс загрузки должен вызвать source.skip (3 МБ в байтах)   -  person Alexander    schedule 17.05.2019


Ответы (2)


ПЕРВЫЙ СПОСОБ

Я пробовал много кодов и, наконец, решил с помощью BufferedSource source = responseBody.source(); source.skip(downloadedSize);

Response request = new Request.Builder().url(url).build();
ResponseBody responseBody = response.body();
BufferedSource source = responseBody.source();

if(isResume)
    source.skip(downloadedSize);

File file = new File(filePath);
BufferedInputStream input = new BufferedInputStream(responseBody.byteStream());
OutputStream output;

if (isResume) {
    output = new FileOutputStream(file, true);
} else {
    output = new FileOutputStream(file, false);    
}

long currentDownloadedSize = 0;
long currentTotalByteSize = responseBody.contentLength();
byte[] data = new byte[1024];
int count = 0;
while ((count = input.read(data)) != -1) {
    currentDownloadedSize += count;
    output.write(data, 0, count);   
}

Это сработало успешно. думаю мне повезло :)

ВТОРОЙ СПОСОБ

Я добавил заголовок для пропуска загруженных байтов, и это сработало.

Request.Builder requestBuilder = new Request.Builder();
if (isResume) {
    requestBuilder.addHeader("Range", "bytes=" + String.valueOf(downloadedSize) + "-");
}
Response request = requestBuilder.url(url).build();
ResponseBody responseBody = response.body();
BufferedSource source = responseBody.source();

File file = new File(filePath);
BufferedInputStream input = new BufferedInputStream(responseBody.byteStream());
OutputStream output;

if (isResume) {
    output = new FileOutputStream(file, true);
} else {
    output = new FileOutputStream(file, false);    
}

long currentDownloadedSize = 0;
long currentTotalByteSize = responseBody.contentLength();
byte[] data = new byte[1024];
int count = 0;
while ((count = input.read(data)) != -1) {
    currentDownloadedSize += count;
    output.write(data, 0, count);   
}
person Alexander    schedule 11.04.2017
comment
Как приостановить загрузку в первую очередь? Можете ли вы поделиться некоторыми идеями о том, как обрабатывать часть возобновления при нажатии кнопки? - person dazed'n'confused; 04.11.2019
comment
@dazed'n'confused, извините, но я не понял вашего вопроса. В чем проблема с возобновлением/паузой? - person Alexander; 04.11.2019
comment
Я пытаюсь загрузить файлы с помощью службы, и мне нужно приостановить загрузку в службе, как мне это сделать? - person dazed'n'confused; 04.11.2019
comment
@dazed'n'confused, вам нужно сохранить последний загруженный размер файла, когда он был приостановлен. При возобновлении вы будете использовать загруженный размер для начала загрузки, с которого байты. На самом деле ответ на ваш вопрос находится в моем ответе stackoverflow.com/a/43356615/1135367 - person Alexander; 07.11.2019
comment
второй способ лучше, в первом случае, когда вы используете пропуск, клиент должен загрузить файл, а затем пропустить N байт с первого. например, вы начинаете загружать файл размером 100 МБ и возобновляете его с 50 МБ, когда вы снова начинаете загрузку клиента сначала, но пропускаете 50 МБ, и после 50 МБ вы можете читать вывод. - person Golil; 17.05.2021

val call = client.newCall(request)

call.enqueue(object : Callback {
    override fun onFailure(call: Call, e: IOException) {
        listener.onFail(e)
    }

    override fun onResponse(call: Call, response: Response) {
        // write the stream to a file (downloading happening)
        val stream = response.body?.byteStream()
    }
})

// this will stop the downloading
call.cancel()

чтобы возобновить использование заголовка «Диапазон» с вашим запросом и добавить поток к уже загруженному файлу.

person Jeeva    schedule 14.04.2020