なぜ一般的なJava Base64でエンコードするライブラリは、エンコーディングのためOutputStreamsと、入力ストリームをエンコードするために使うのですか?

M.ウォレス:

私は、我々はファイル全体をメモリにロードしているJavaプログラムでメモリの問題を解決しようとしているbase64では、それをコードして、ポストの要求におけるフォームパラメータとしてそれを使用します。これは非常に大きなファイルサイズに原因OOMEです。

私は、私は、HTTP POSTリクエストのリクエストボディに、base64でエンコーダを通じてファイルをストリーミングすることができる午前ソリューションに取り組んでいます。私は人気のエンコーディングライブラリ(グアバ、java.util.Base64、android.util.Base64とorg.apache.batik.util)のすべてに気づいた一般的なパターンの一つということであれば、ライブラリサポートはストリームでエンコード、エンコーディングいつものOutputStreamを介して行われ、デコードはいつものInputStreamを介して行われます。

私はこれらの決定の背後にある理由を決定/トラブル発見を持っています。これらの人気があり、よく書かれたライブラリの多くは、このAPIの設計に合わせて、私はこの理由があることを前提としていることを考えます。入力ストリームになるかInputStreamを受け入れるためにこれらのデコーダのいずれかを適応させることは非常に困難でいないようですが、これらのエンコーダは、このように設計されている有効なアーキテクチャの理由がある場合、私は疑問に思って。

なぜ共通ライブラリは、InputStreamを使用してデコードOuputStreamとのBase64てBase64エンコードを行うのですか?

私の主張をバックアップする例:

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

特に、任意のバッファが存在しない場合のストリームの、そしてもちろんの連鎖に注意一切を私は(場合には、HTTP GETの代わりに使用したい)URLセーフベース64を使用しました。


あなたのケースでは、もちろん、あなたがURLと使ってHTTP POSTリクエスト生成することができ、直接エンコード、検索にOutputStreamそれをラップすることにより、ストリームを。何ベース64の符号化データは、(広く)する必要はありませんそのように緩衝化。取得する方法の例を参照してくださいOutputStream ここに

あなたがバッファリングする必要がある場合、あなたはそれが間違ってやっている、覚えておいてください。

コメントで述べたように、HTTP POSTは、今あなたがHTTP接続に直接ベース64をコード化することができる方法を知って、ベース64のエンコーディングが、何を必要としません。


java.util.Base64特定注:ベース64はテキストであるが、BASE64ストリームは/消費バイトを生成します。それは単にASCIIエンコーディング(UTF-16テキストについては、このことができる楽しさ)を前提としています。個人的に私は、これはひどい設計上の決定だと思います。彼らはラップしているはずですReaderし、Writer代わりに、ダウンそれが遅くなるが、わずかにエンコードする場合でも。

彼らの防衛に、様々なベース64の規格とRFCも、この間違ったを取得します。

おすすめ

転載: http://10.200.1.11:23101/article/api/json?id=478603&siteId=1