Почему популярные кодирующие библиотеки Java Base64 использовать OutputStreams для кодирования и InputStreams для кодирования?

М. Уоллес:

Я пытался решить проблему памяти в программе Java, где мы загружаем весь файл в память, base64 кодирования его, а затем использовать его в качестве параметра формы в запросе поста. Это вызывает OOME из-за чрезвычайно большого размера файла.

Я работаю над решением , где я в состоянии потока файла через base64 кодер, в теле запроса текста запроса HTTP Post. Одним из наиболее распространенных моделей я заметил во всех популярных библиотеках кодирования (гуава, java.util.Base64, android.util.Base64 и org.apache.batik.util) является то , что если библиотека поддерживает кодирование с Streams, кодирование всегда делается через OutputStream и декодирование всегда выполняется через InputStream.

У меня возникли проблемы с поиском / определение обоснования этих решений. Учитывая, что многие из этих популярных и хорошо написанных библиотек выравнивать с этим проектом апите, я полагаю, что есть причина для этого. Это, кажется, не очень сложно адаптировать один из этих декодеров, чтобы стать InputStream или принять в InputStream, но мне интересно, если есть действительная архитектурная причина кодеры разработаны таким образом.

Почему общие библиотеки делают Base64 кодирование через OuputStream и Base64 декодирования через InputStream?

Примеры для резервного копирования моих требований:

java.util.Base64
 - Base64.Decoder.wrap(InputStream stream)
 - Base64.Encoder.wrap(OutputStream stream)

android.util.Base64
 - Base64InputStream  // An InputStream that does Base64 decoding on the data read through it.
 - Base64OutputStream // An OutputStream that does Base64 encoding

google.common.io.BaseEncoding
 - decodingStream(Reader reader)
 - encodingStream(Writer writer)

org.apache.batik.util
 - Base64DecodeStream implements InputStream
 - Base64EncodeStream implements OutputStream

Мартин Bodewes:

Ну да, вы можете повернуть его вспять, но это имеет наибольший смысл. Base64 используется для двоичных данных - генерируется или оперировал приложение - совместим с внешней средой на основе текста. Таким образом, база 64 кодированных данные всегда требуется на внешней стороне и декодированные двоичные данные требуются на внутренней стороне .

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


Если вы хотите экспортировать двоичные данные наружу, естественно, вы бы использовать выходной поток. Если что данные должны быть закодированы в базе 64, вы убедитесь, что вы посылаете данные в выходной поток, который кодирует к основанию 64.

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


Позволяет создать немного картины. Скажем, у вас есть приложение, которое работает в текстовом ориентированной среде, но работает на двоичных данных. Важной частью является направление стрелок из контекста приложения слева.

После этого вы получаете для ввода (чтения вызовов):

{APPLICATION} <- (binary data decoding) <- (base64 decoding) <- (file input stream) <- [BASE 64 ENCODED FILE]

для этого вы, естественно, использовать входные потоки.

Так выглядят ДАВАЙТЕ на выходных (звонки записи):

{APPLICATION} -> (binary data encoding) -> (base64 encoding) -> (file output stream) -> [BASE 64 ENCODED FILE]

для этого вы, естественно, использовать выходные потоки.

Эти поток могут быть соединены друг с другом посредством сцепления их вместе , то есть , используя один поток в качестве родителя другого потока.


Вот пример в Java. Обратите внимание, что создание двоичного кодера / декодера в самом классе данных является немного некрасиво; как правило, вы будете использовать другой класс для этого - я надеюсь, что это достаточно для демонстрационных целей.

import static java.nio.charset.StandardCharsets.UTF_8;

import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.Base64;

public class BinaryHandlingApplication {

    /**
     * A data class that encodes to binary output, e.g. to interact with an application in another language.
     * 
     * Binary format: [32 bit int element string size][UTF-8 element string][32 bit element count]
     * The integers are signed, big endian values.
     * The UTF-8 string should not contain a BOM.
     * Note that this class doesn't know anything about files or base 64 encoding.
     */
    public static class DataClass {
        private String element;
        private int elementCount;

        public DataClass(String element) {
            this.element = element;
            this.elementCount = 1;
        }

        public String getElement() {
            return element;
        }

        public void setElementCount(int count) {
            this.elementCount = count;
        }

        public int getElementCount() {
            return elementCount;
        }

        public String toString() {
            return String.format("%s count is %d", element, elementCount);
        }

        public void save(OutputStream out) throws IOException {

            DataOutputStream dataOutputStream = new DataOutputStream(out);

            // so here we have a chain of:
            // a dataoutputstream on a base 64 encoding stream on a fileoutputstream 


            byte[] utf8EncodedString = element.getBytes(UTF_8);
            dataOutputStream.writeInt(utf8EncodedString.length);
            dataOutputStream.write(utf8EncodedString);

            dataOutputStream.writeInt(elementCount);
        }

        public void load(InputStream in) throws IOException {
            DataInputStream dataInputStream = new DataInputStream(in);

            // so here we have a chain of:
            // a datainputstream on a base 64 decoding stream on a fileinputstream 

            int utf8EncodedStringSize = dataInputStream.readInt();
            byte[] utf8EncodedString = new byte[utf8EncodedStringSize];
            dataInputStream.readFully(utf8EncodedString);
            this.element = new String(utf8EncodedString, UTF_8);

            this.elementCount = dataInputStream.readInt();
        }

    }

    /**
     * Create the a base 64 output stream to a file; the file is the text oriented
     * environment.
     */
    private static OutputStream createBase64OutputStreamToFile(String filename) throws FileNotFoundException {
        FileOutputStream textOutputStream = new FileOutputStream(filename);
        return Base64.getUrlEncoder().wrap(textOutputStream);
    }

    /**
     * Create the a base 64 input stream from a file; the file is the text oriented
     * environment.
     */
    private static InputStream createBase64InputStreamFromFile(String filename) throws FileNotFoundException {
        FileInputStream textInputStream = new FileInputStream(filename);
        return Base64.getUrlDecoder().wrap(textInputStream);
    }

    public static void main(String[] args) throws IOException {
        // this text file acts as the text oriented environment for which we need to encode
        String filename = "apples.txt";

        // create the initial class
        DataClass instance = new DataClass("them apples");
        System.out.println(instance);

        // perform some operation on the data
        int newElementCount = instance.getElementCount() + 2;
        instance.setElementCount(newElementCount);

        // write it away
        try (OutputStream out = createBase64OutputStreamToFile(filename)) {
            instance.save(out);
        }

        // read it into another instance, who cares
        DataClass changedInstance = new DataClass("Uh yeah, forgot no-parameter constructor");
        try (InputStream in = createBase64InputStreamFromFile(filename)) {
            changedInstance.load(in);
        }
        System.out.println(changedInstance);
    }
}

Особенно отметит цепочку из потоков и, конечно , отсутствия каких - либо буферов бы то ни было . Я использовал URL безопасной базы 64 (в случае , если вы хотите использовать HTTP GET вместо этого).


В вашем случае, конечно же , вы можете создать запрос HTTP POST с использованием URL и непосредственно закодировать в извлеченном OutputStreamпотоке, окружив его. Таким образом , нет базы 64 закодированных данных потребности, чтобы быть (широко) в буфере. Смотрите примеры о том , как добраться до OutputStream сюда .

Помните, что если вам нужно в буфер, вы делаете это неправильно.

Как уже упоминалось в комментариях, HTTP POST не требуется база 64 кодирования, но то, что, теперь вы знаете, как вы можете кодировать основание 64 непосредственно к соединению HTTP.


java.util.Base64удельная примечание: Хотя базовая 64 представляет собой текст, поток генерирует base64 / потребляет байт; он просто предполагает кодировку ASCII (это может быть интересно для UTF-16 текста). Лично я считаю , что это ужасно дизайнерское решение; они должны были завернутые Readerи Writerвместо того, чтобы , даже если это замедляет немного кодирующие.

Для их защиты, различные базы 64 стандартов и RFC также получить это неправильно.

рекомендация

отhttp://10.200.1.11:23101/article/api/json?id=478600&siteId=1