在Java中,
String
≠ byte[]
。
byte []
表示原始二进制数据。
String
表示文本,具有关联的字符集/编码,以便能够告诉它表示哪些字符。
二进制数据≠文本。
String
内的文本数据具有Unicode / UTF-16作为字符集/编码(或在序列化时为Unicode / mUTF-8)。每当您从不是String
的东西转换为String
或反之亦然时,都需要为非String
文本数据指定一个字符集/编码(即使您使用隐式地执行此操作 使用平台的默认字符集)。
PNG文件包含表示图像(和相关元数据)的原始二进制数据,而不是文本。因此,您不应将其视为文本。
\ x89PNG
不是文本,只是用于标识PNG文件的“魔术”标题。 0x89
甚至不是字符,只是任意字节值,其唯一的理智表示形式显示是类似于 \ x89
, 0x89
,...同样,其中的PNG
在现实中是二进制数据,它也可能是0xdeadbeef
,它不会改变任何东西。 PNG
之所以可以由人类阅读,只是一种方便。
您的问题源于您的协议混合了文本和二进制数据,而Java(与一些其他语言(如C)不同)将二进制数据与文本区分对待。
Java为读取二进制数据提供* InputStream
,并为读取文本提供* Reader
。我看到处理输入的两种方法:
如果您需要缓冲,第二种情况下正确的放置位置是在*Reader
下面。如果您使用了BufferedReader
,则BufferedReader
可能会从InputStream
中消耗更多的输入,因此您将会得到以下代码:
┌───────────────────┐
│ InputStreamReader │
└───────────────────┘
↓
┌─────────────────────┐
│ BufferedInputStream │
└─────────────────────┘
↓
┌─────────────┐
│ InputStream │
└─────────────┘
您需要使用
InputStreamReader
读取文本,然后使用
BufferedInputStream
从同一流中读取适当数量的二进制数据。
一个问题是识别"\r"
(旧版MacOS)和"\r\n"
(DOS / Windows)作为行终止符。在这种情况下,您可能会多读一个字符。您可以采取已弃用的DataInputStream.readline()
方法所采用的方法:将内部InputStream
透明地包装成PushbackInputStream
并取消读取该字符。
但是,由于您似乎没有Content-Length,因此我建议采用第一种方式,将所有内容视为二进制,并在读取整行后仅转换为String
。在这种情况下,我将MIME分隔符视为二进制数据。
输出:
由于您正在处理二进制数据,因此不能只使用println()
。 PrintStream
具有write()
方法,可以处理二进制数据(例如:用于输出到二进制文件)。
或者,您的数据必须在将其视为文本的通道上传输。 Base64专为这种情况而设计(将二进制数据作为ASCII文本传输)。 Base64编码形式仅使用US_ASCII字符,因此您应该能够在任何是US_ASCII的超集(ISO-8859- *,UTF-8,CP-1252等)的字符集/编码中使用它。由于您正在将二进制数据转换为/从文本,因此Base64的唯一合理API应该是:
String Base64Encode(byte[] data);
byte[] Base64Decode(String encodedData);
这基本上就是内部java.util.prefs.Base64
使用的内容。
结论:
在Java中,String
≠ byte[]
。
二进制数据 ≠ 文本。