为什么流行的 Java Base64 编码库使用 OutputStreams 进行编码,而使用 InputStreams 进行编码?
Why do popular Java Base64 encoding libraries use OutputStreams for Encoding and InputStreams for encoding?
我一直在尝试解决 Java 程序中的内存问题,我们将整个文件加载到内存中,对其进行 base64 编码,然后将其用作 post 中的表单参数要求。这是由于文件太大而导致 OOME 的原因。
我正在研究一个解决方案,我可以通过 base64 编码器将文件流式传输到 Http Post 请求的请求正文中。我在所有流行的编码库(Guava、java.util.Base64、android.util.Base64 和 org.apache.batik.util)中注意到的一种常见模式是 if 库支持使用 Streams 进行编码,编码始终通过 OutputStream 完成,解码始终通过 InputStream 完成。
我无法 finding/determining 这些决定背后的原因。鉴于这么多流行且编写良好的库与这种 api 设计保持一致,我认为这是有原因的。
将这些解码器之一改编成 InputStream 或接受 InputStream 似乎并不难,但我想知道这些编码器以这种方式设计是否有有效的体系结构原因。
为什么普通库通过OuputStream进行Base64编码,通过InputStream进行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
嗯,是的,你可以逆转它,但这是最有意义的。 Base64 用于使 二进制数据 - 由应用程序生成或操作 - 与基于文本的外部环境兼容。 所以外面总是需要base 64编码的数据,里面总是需要解码后的二进制数据。
应用程序通常不会对 base 64 encoded 数据本身执行任何操作;只需要与另一个应用程序通信二进制数据当需要或期望文本界面时。
如果你想把你的二进制数据导出到外部,自然要用到输出流。如果该数据需要以 base 64 编码,请确保将数据发送到编码为 base 64 的输出流。
如果您想从外部导入二进制数据,那么您可以使用输入流。如果该数据以 base 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 中的示例。请注意,在数据 class 中创建二进制 encoder/decoder 本身有点难看;通常你会为此使用另一个 class - 我希望它足以用于演示目的。
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-safe base 64(如果您想改用 HTTP GET)。
在您的情况下,当然,您可以使用 URL 生成 HTTP POST 请求,并且 直接编码 到检索到的 OutputStream
通过包装流式传输。这样就不需要(大量)缓冲 base 64 编码数据。查看有关如何到达 OutputStream
here.
的示例
记住,如果你需要缓冲,那是你做错了。
如评论中所述,HTTP POST 不需要 base 64 编码,但无论如何,现在您知道如何将 base 64 直接编码到 HTTP 连接。
java.util.Base64
具体说明:
尽管 base 64 是文本,但 base64 流生成/消耗字节;
它只是假定 ASCII 编码(这对于 UTF-16 文本可能很有趣)。
我个人认为这是一个糟糕的设计决定;他们应该用 Reader
和 Writer
代替,即使这会稍微减慢编码速度。
为他们辩护,各种 base 64 标准和 RFC 也犯了这个错误。
我一直在尝试解决 Java 程序中的内存问题,我们将整个文件加载到内存中,对其进行 base64 编码,然后将其用作 post 中的表单参数要求。这是由于文件太大而导致 OOME 的原因。
我正在研究一个解决方案,我可以通过 base64 编码器将文件流式传输到 Http Post 请求的请求正文中。我在所有流行的编码库(Guava、java.util.Base64、android.util.Base64 和 org.apache.batik.util)中注意到的一种常见模式是 if 库支持使用 Streams 进行编码,编码始终通过 OutputStream 完成,解码始终通过 InputStream 完成。
我无法 finding/determining 这些决定背后的原因。鉴于这么多流行且编写良好的库与这种 api 设计保持一致,我认为这是有原因的。 将这些解码器之一改编成 InputStream 或接受 InputStream 似乎并不难,但我想知道这些编码器以这种方式设计是否有有效的体系结构原因。
为什么普通库通过OuputStream进行Base64编码,通过InputStream进行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
嗯,是的,你可以逆转它,但这是最有意义的。 Base64 用于使 二进制数据 - 由应用程序生成或操作 - 与基于文本的外部环境兼容。 所以外面总是需要base 64编码的数据,里面总是需要解码后的二进制数据。
应用程序通常不会对 base 64 encoded 数据本身执行任何操作;只需要与另一个应用程序通信二进制数据当需要或期望文本界面时。
如果你想把你的二进制数据导出到外部,自然要用到输出流。如果该数据需要以 base 64 编码,请确保将数据发送到编码为 base 64 的输出流。
如果您想从外部导入二进制数据,那么您可以使用输入流。如果该数据以 base 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 中的示例。请注意,在数据 class 中创建二进制 encoder/decoder 本身有点难看;通常你会为此使用另一个 class - 我希望它足以用于演示目的。
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-safe base 64(如果您想改用 HTTP GET)。
在您的情况下,当然,您可以使用 URL 生成 HTTP POST 请求,并且 直接编码 到检索到的 OutputStream
通过包装流式传输。这样就不需要(大量)缓冲 base 64 编码数据。查看有关如何到达 OutputStream
here.
记住,如果你需要缓冲,那是你做错了。
如评论中所述,HTTP POST 不需要 base 64 编码,但无论如何,现在您知道如何将 base 64 直接编码到 HTTP 连接。
java.util.Base64
具体说明:
尽管 base 64 是文本,但 base64 流生成/消耗字节;
它只是假定 ASCII 编码(这对于 UTF-16 文本可能很有趣)。
我个人认为这是一个糟糕的设计决定;他们应该用 Reader
和 Writer
代替,即使这会稍微减慢编码速度。
为他们辩护,各种 base 64 标准和 RFC 也犯了这个错误。