在 Java 中,String ≠ byte[]。
-
byte[] 代表原始二进制数据。
-
String 代表文本,它具有关联的字符集/编码,以便能够分辨它代表哪些字符。
二进制数据≠文本。
String 中的文本数据具有 Unicode/UTF-16 作为字符集/编码(或 Unicode/mUTF-8 序列化时)。每当您从不是String 的内容转换为String 或反之亦然时,您需要为非String 文本数据指定字符集/编码(即使您使用 隐式执行此操作)平台的默认字符集)。
PNG 文件包含表示图像(和相关元数据)的原始二进制数据,不是文本。因此,您不应将其视为文本。
\x89PNG 不是文本,它只是一个用于识别 PNG 文件的“神奇”标题。 0x89 甚至不是一个字符,它只是一个任意字节值,它对 display 的唯一合理表示是 \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[]。
二进制数据≠文本。