it-roy-ru.com

Как BufferedReader читает файлы с S3?

У меня очень большой файл (несколько ГБ) в AWS S3, и мне нужно только небольшое количество строк в файле, которые удовлетворяют определенному условию. Я не хочу загружать весь файл в память, а затем искать и печатать эти несколько строк - загрузка памяти для этого будет слишком высокой. Правильный путь - загружать в память только те строки, которые необходимы.

Согласно документации AWS для чтения из файла :

fullObject = s3Client.getObject(new GetObjectRequest(bucketName, key));
 displayTextInputStream(fullObject.getObjectContent());

private static void displayTextInputStream(InputStream input) throws IOException {
    // Read the text input stream one line at a time and display each line.
    BufferedReader reader = new BufferedReader(new InputStreamReader(input));
    String line = null;
    while ((line = reader.readLine()) != null) {
        System.out.println(line);
    }
    System.out.println();
}

Здесь мы используем BufferedReader . Мне не ясно, что здесь происходит.

Делаем ли мы сетевой вызов S3 каждый раз, когда читаем новую строку, и сохраняем только текущую строку в буфере? Или весь файл загружается в память и затем читается построчно с помощью BufferedReader? Или это где-то посередине?

5
sbhatla

Один из ответов на ваш вопрос уже содержится в документации, которую вы связали:

Ваше сетевое соединение остается открытым, пока вы не прочитаете все данные или не закроете поток ввода.

BufferedReader не знает, откуда берутся данные, которые он читает, потому что вы передаете ему другую Reader. BufferedReader создает буфер определенного размера (например, 4096 символов) и заполняет этот буфер, читая из базового Reader, прежде чем начинать раздавать данные вызовов read() или read(char[] buf).

Reader, который вы передаете BufferedReader, кстати, использует для себя другой буфер для преобразования из потока на основе byte в считыватель на основе char. Он работает так же, как и с BufferedReader, поэтому внутренний буфер заполняется чтением из переданной InputStream, которая является InputStream, возвращаемой вашим S3-клиентом.

Что именно происходит внутри этого клиента, если вы пытаетесь загрузить данные из потока, зависит от реализации. Один из способов - оставить одно сетевое соединение открытым, и вы можете читать с него по своему усмотрению, иначе сетевое соединение может быть закрыто после того, как часть данных будет прочитана, а новое откроется, когда вы попытаетесь получить следующее.

В приведенной выше документации, похоже, говорится, что у нас есть прежняя ситуация, поэтому: Нет, вызовы readLine не приводят к одиночным сетевым вызовам.

И чтобы ответить на другой ваш вопрос: Нет, BufferedReader, InputStreamReader и, скорее всего, InputStream, возвращенные S3-клиентом, не загружаются в память всего документа. Это противоречило бы цели использования потоков в первую очередь, и клиент S3 мог бы просто вместо этого возвращать byte[][] (чтобы обойти ограничение в 2 ^ 32 байта на массив byte-)

Правка: есть исключение из последнего абзаца. Если в большом документе размером в гигабайты нет разрывов строк, вызов readLine на самом деле приведет к считыванию всех данных в память (и, скорее всего, к OutOfMemoryError). Я взял «обычный» текстовый документ, отвечая на ваш вопрос.

7
Lothar

Если вы в основном не ищете конкретное слово/слова и знаете диапазон байтов, вы также можете использовать заголовок Range в S3. Это должно быть особенно полезно, так как вы работаете с одним файлом размером в несколько ГБ. Задание диапазона не только помогает уменьшить объем памяти, но и быстрее, так как читается только указанная часть файла. 

См. Существует ли «функция чтения диапазона S3», позволяющая считывать назначенный диапазон байтов из файла AWS-S3?

Надеюсь это поможет.

Sreram 

3
Sreram

Зависит от размера строк в вашем файле. readLine () будет продолжать строить строку, извлекающую данные из потока в блоках размером с размер вашего буфера, пока вы не нажмете символ завершения строки. Таким образом, используемая память будет порядка вашей строки + длина буфера.

0
Jesse

В инфраструктуру AWS делается только один HTTP-вызов, и данные считываются в память небольшими блоками, размер которых может варьироваться и не находится под вашим непосредственным контролем.

Это уже очень эффективно использует память, предполагая, что каждая строка в файле имеет достаточно маленький размер. 

Один из способов дальнейшей оптимизации (для сетевых и вычислительных ресурсов), при условии, что ваше «определенное условие» представляет собой простое совпадение строк, заключается в использовании S3 Select: https://aws.Amazon.com/s3/features/#s3 -Выбрать

0
Alex R