Загрузка файла Okio/Okhttp с использованием BufferedSink и декодирование Base64 без многократного хранения всего файла в памяти

Получил небольшую проблему atm. для моего обновления «inapp» я загружаю новый .apk с кодировкой base64 из моего веб-пространства. У меня функционал сильно упал, это код без декодирования.

            public void onResponse(Call call, Response response) throws IOException {
            if(response.isSuccessful()){
                ResponseBody body = response.body();
                BufferedSource source = body.source();
                source.request(Long.MAX_VALUE);
                Buffer buffer = source.buffer();

                String rString = buffer.clone().readString(Charset.forName("UTF-8"));
                Log.i("Test: ", AppUtils.decodeBase64(rString));

                if(rString.equals("xxx")){
                    EventBus.getDefault().post(new KeyNotValid());
                    dispatcher.cancelAll();
                }else{
                    EventBus.getDefault().post(new SaveKey(apikey));
                    BufferedSink sink = Okio.buffer(Okio.sink(myFile));
                    sink.writeAll(source);
                    sink.flush();
                    sink.close();
                }
            }
        }

Буфер/журнал на самом деле не нужен, он просто используется для проверки ответа во время тестирования.

Как мне расшифровать байты, прежде чем я запишу их в приемник? Пробовал делать через. ByteString, но я не смог найти способ записать декодированную строку обратно в BufferedSource.

Большинство альтернатив довольно медленные, такие как повторное открытие файла после этого, чтение байтов в память, декодирование и запись их обратно.

Был бы очень признателен за любую помощь в этом

ваше здоровье


person p_d    schedule 02.01.2018    source источник


Ответы (2)


Вы уже можете использовать ответ как InputStream через ResponseBody.byteStream. Вы можете украсить этот поток с помощью https://commons.apache.org/proper/commons-codec/apidocs/org/apache/commons/codec/binary/Base64InputStream.html и использовать его для чтения потока байтов и записи его в приемник для файла кусками.

person Yuri Schimke    schedule 03.01.2018

Я знаю, что этот ответ приходит довольно поздно и что ответ Юрия технически правильный, но я думаю, что самый идиоматический способ сделать это — воспользоваться шаблоном композиции, продвигаемым Okio, для создания Source, который декодирует из Base64 (или Sink, который кодирует в Base64, если вам это нужно).

Вот небольшое доказательство концепции (я уверен, что ее можно улучшить):

public class Base64Source implements Source {

    private Source delegate;
    private Base64.Decoder decoder; // Using Java 8 API, but it can be any library

    public Base64Source(Source delegate) {
        this(delegate, Base64.getDecoder());
    }

    public Base64Source(Source delegate, Base64.Decoder decoder) {
        this.delegate = delegate;
        this.decoder = decoder;
    }

    @Override
    public long read(Buffer sink, long byteCount) throws IOException {
        Buffer buffer = new Buffer();
        long actualRead = this.delegate.read(buffer, byteCount);
        if (actualRead == -1) {
            return -1;
        }

        byte[] encoded = buffer.readByteArray(actualRead);
        byte[] decoded = decoder.decode(encoded);
        sink.write(decoded);

        return decoded.length;
    }

    @Override
    public Timeout timeout() {
        return this.delegate.timeout();
    }

    @Override
    public void close() throws IOException {
        this.delegate.close();
    }
}

И вот как это можно использовать

BufferedSource source = Okio.buffer(new Base64Source(originalSource));
BufferedSink sink = ... // create sink
sink.writeAll(source);

// Don't forget to close the source/sink to flush and free resources
sink.close();
source.close();
person user2340612    schedule 27.02.2018