¿Por qué los populares de Java base 64 bibliotecas que codifican utilizan para codificar y OutputStreams InputStreams para codificar?

M. Wallace:

He estado tratando de resolver un problema de memoria en un programa Java, donde estamos cargando un archivo completo en la memoria, la codificación Base64 y luego usarlo como un parámetro de forma en una solicitud posterior. Esta es la causa OOME debido al tamaño de archivo muy grande.

Estoy trabajando en una solución en la que soy capaz de transmitir los archivos a través de un codificador de base 64, en el cuerpo de la petición de una petición HTTP Post. Uno de los patrones comunes que he notado en todas las bibliotecas de codificación populares (guayaba, java.util.Base64, android.util.Base64 y org.apache.batik.util) es que si los soportes de la biblioteca con la codificación arroyos, la codificación siempre se realiza a través de un OutputStream y la decodificación se realiza siempre a través de un InputStream.

Estoy teniendo problemas para encontrar / determinar el razonamiento detrás de estas decisiones. Teniendo en cuenta que muchas de estas bibliotecas populares y bien escritos se alinean con este diseño api, supongo que hay una razón para ello. No parece muy difícil de adaptar uno de estos decodificadores para convertirse en un InputStream o aceptar un InputStream, pero me pregunto si hay una razón válida arquitectónica estos codificadores están diseñados de esta manera.

¿Por qué las bibliotecas comunes hacen codificación Base64 a través de un OuputStream y decodificar Base64 través de un InputStream?

Los ejemplos para respaldar mis afirmaciones:

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

Martin Bodewes:

Bueno, sí, se puede revertir, pero esto tiene más sentido. Base 64 se utiliza para hacer los datos binarios - genera o operado por la aplicación - compatible con un entorno exterior basado en texto. Así los datos base 64 codificados siempre se requiere en el exterior y no se requiere que los datos binaria decodificada en el interior.

Una aplicación generalmente no lleva a cabo ninguna operación en la base 64 codificada propios datos; sólo se necesita para comunicar datos binarios con otra aplicación cuando se requiere o se espera una interfaz de texto .


Si desea exportar sus datos binarios al exterior, naturalmente, que usaría un flujo de salida. Si que las necesidades de datos para ser codificados en base 64, se asegura de que envíe los datos a un flujo de salida que codifica a la base 64.

Si desea importar los datos binarios desde el exterior, entonces sería utilizar un flujo de entrada. Si esos datos está codificado en base 64 a continuación, primero tiene que decodificarlo, por lo que asegúrese de que descodificarlo antes de tratarla como una secuencia binaria.


Vamos a crear un poco de una imagen. Digamos que tienes una aplicación que opera en un entorno orientado textual, pero opera sobre datos binarios. La parte importante es la dirección de las flechas desde el contexto de la aplicación de la izquierda.

Luego se llega a la entrada (leer llamadas):

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

para ello se utiliza de forma natural flujos de entrada.

Así Echemos un vistazo a la salida (llamadas de escritura):

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

para esto se utiliza de forma natural flujos de salida.

Estos corriente se puede conectar entre sí mediante el encadenamiento de ellos juntos , es decir, utilizando una corriente como padre de la otra corriente.


Este es un ejemplo en Java. Tenga en cuenta que la creación de la binaria del codificador / decodificador en la propia clase de datos es un poco feo; en general, se usaría otra clase para la que - espero que basta con fines de demostración.

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);
    }
}

Tenga en cuenta especialmente el encadenamiento de las corrientes y por supuesto la ausencia de cualquier buffer de ningún tipo . He usado base URL segura 64 (en caso de que desee utilizar en lugar de HTTP GET).


En su caso, por supuesto, se podría generar una solicitud POST HTTP utilizando una URL y directamente codifican a la recuperada OutputStreamcorriente envolviéndolo. De esta manera no hay base 64 necesidades de datos codificados para ser (ampliamente) tamponado. Ver ejemplos de cómo llegar a la OutputStream aquí .

Recuerde, si usted necesita para amortiguar, lo estás haciendo mal.

Como se mencionó en los comentarios, HTTP POST no necesitan codificación base 64 pero cualquiera que sea, ya sabes cómo se puede codificar base 64 directamente a una conexión HTTP.


java.util.Base64nota específica: A pesar de base 64 es texto, la corriente de base 64 genera / consume bytes; simplemente asume la codificación ASCII (esto puede ser divertido para UTF-16 texto). Personalmente creo que esto es una decisión de diseño terribles; que deberían haber envuelto una Readery Writeren su lugar, incluso si que ralentiza la codificación ligeramente.

Para su defensa, las diversas normas de base 64 y RFC también consigue este mal.

Supongo que te gusta

Origin http://10.200.1.11:23101/article/api/json?id=478606&siteId=1
Recomendado
Clasificación