Java和c#之间的Base64和二进制流

本文关键字:二进制 Base64 之间 Java | 更新日期: 2023-09-27 18:14:23

我觉得答案是显而易见的,但假设我在c#中有以下内容

using (MemoryStream ms = new MemoryStream())
{
    using (BinaryWriter bw = new BinaryWriter(ms))
    {
        // Write some floats, bytes, and uints
        // Convert.ToBase64String this stuff from ms.ToArray
    }
}

和Java中的以下内容(好吧,它是Scala,但使用Java库):

val byteStream = new ByteArrayOutputStream()
val outStream = new DataOutputStream(byteStream)
// Write some floats, bytes, and longs where the uints were using 
// writeFloat, writeByte, and writeLong. .NET has an overloaded 
// function that takes whatever. 
// Base64.getEncoder.encodeToString byteStream.toByteArray

得到完全不同的64基字符串。他们在这里做的有什么不同?我需要Java输出与。net输出匹配。我认为这是某种字节排序问题,但我没有任何运气使用ByteBuffer来纠正这个问题。

Java:

PczMzT3MzM0/gAAAPczMzQAAAAAAAAAAAAAAAD3MzM0 gAAAAQAAAABRn8XzAAAAAAAAAAEAAAAAAAAAAQ = =

c#(由于某些原因,我们把它们去掉了):

zczMPc3MzD0AAIA/zczMPQAAAAAAAAAAAAAAAM3MzD0AAIA AfPFn1EBAAAAAQAAAA

我真的觉得好像是字节排序,这就是为什么我尝试在Java代码中使用ByteBuffer, order方法来改变排序,但我没有成功。

为了进一步明确,Java代码运行在x86_64 CentOS Java 7上,而。net运行在x86_64 Windows Server 2008 . net 4上。这些值来自Protobuf对象,所以我认为它们应该是跨平台的。从数字上讲,无论我输入什么,至少当我至少写入这三种数据类型时,数据都是相同和一致的。唯一显着的区别是Java中缺乏无符号类型,也许存在二进制表示差异,这是我最初试图解决的问题,但我似乎无法弄清楚。

我已经说过了。不能使用其他格式。我需要二进制数据从java编写,然后基64编码的结果与。net相同的结果。序列化选择不是一个选项。一定是这个。我需要一个资源来帮助我把这些整合在一起,不管这是否意味着字节数据的二进制操作。我需要对数据类型进行一些解释,因为我已经进行了大量的搜索,但没有找到解释如何做到这一点或真正的差异是什么的资源,所以我可以实现一个解决方案,我决定在这里问。

Java和c#之间的Base64和二进制流

主要问题是c#的BinaryWriter首先写入数据类型的低字节,而Java的DataOutputStream首先写入高字节。

同样,当你写一个。net无符号整数时,它写4个字节。但是当您编写Java long时,它写入8字节。这是另一个不同之处

但是,一旦你理解了它们之间的差异,将它们匹配起来实际上并不难。下面是两个代码片段,一个用c#编写,另一个用Java编写,它们编码相同的信息并输出相同的Base64编码字符串。在我的例子中,我选择重写Java编写float s和long s的方式。

。. NET代码示例

static void Main(string[] args)
{
    using (MemoryStream ms = new MemoryStream())
    {
        using (BinaryWriter bw = new BinaryWriter(ms))
        {
            // floats
            bw.Write(-456.678f);
            bw.Write(0f);
            bw.Write(float.MaxValue);
            // bytes
            bw.Write((byte)0);
            bw.Write((byte)120);
            bw.Write((byte)255);
            // uints
            bw.Write(0U);
            bw.Write(65000U);
            bw.Write(4294967295U);
        }
        var base64String = Convert.ToBase64String(ms.ToArray());
        Console.WriteLine(base64String);
    }
}
Java代码示例
public static void main(String[] args) throws Exception {
    try (ByteArrayOutputStream byteStream = new ByteArrayOutputStream()) {
        try (DataOutputStream outStream = new DataOutputStream(byteStream)) {
            // floats
            writeFloat(-456.678f, outStream);
            writeFloat(0f, outStream);
            writeFloat(Float.MAX_VALUE, outStream);
            // bytes
            outStream.writeByte(0);
            outStream.writeByte(120);
            outStream.writeByte(255);
            // longs (uints)
            writeUint(0L, outStream);
            writeUint(65000L, outStream);
            writeUint(4294967295L, outStream);
        }
        String base64String = Base64.getEncoder().encodeToString(byteStream.toByteArray());
        System.out.println(base64String);
    }
}
private static void writeFloat(float f, DataOutputStream stream) throws Exception {
    int val = Float.floatToIntBits(f);
    stream.writeByte(val & 0xFF);
    stream.writeByte((val >>> 8) & 0xFF);
    stream.writeByte((val >>> 16) & 0xFF);
    stream.writeByte((val >>> 24) & 0xFF);
}
private static void writeUint(long val, DataOutputStream stream) throws Exception {
    stream.writeByte((int) (val & 0xFF));
    stream.writeByte((int) ((val >>> 8) & 0xFF));
    stream.writeByte((int) ((val >>> 16) & 0xFF));
    stream.writeByte((int) ((val >>> 24) & 0xFF));
}

两个样本的输出

39/AHj/AAAAAOj9AAD yVbkwwAAAAD///////

确保使用float类型测试边缘用例,并在必要时进行调整。如果这对你很重要,我希望像NaN这样有趣的值会引起差异,但也许你并不关心。否则,我希望它能正常工作。

如何实现跨平台二进制通信:

  • 定义精确的字节格式
  • 在各平台的实现

通常,您可以通过使用接近您需求的现成协议(如https://en.wikipedia.org/wiki/BSON)并在您感兴趣的一个或所有平台上支持来简化这两个步骤。

请注意,通常给定语言/框架中的基本二进制序列化类型严格针对该语言/框架(通常是特定版本),因为它经常提供速度/大小优势,并且没有公认的"二进制对象表示"标准。

另一种方法是使用其他答案建议的定义良好的文本格式,如JSON/XML。

二进制格式之间的一些可能的技术差异:

  • 整数类型的序列化可以根据字节顺序或可能的替代表示(如。net中的压缩int)而有所不同
  • 布尔类型和枚举类型的大小可以不同
  • 数组/字符串可以使用不同的类型来表示长度
  • 填充可以通过一些二进制表示来添加
  • 字符串可以是Utf8, Utf-16或任何其他指定/未指定的编码,带或不带尾0。

不同的平台有不同的二进制表示。如果你想匹配base64字符串,你应该使用json或xml序列化。Json或xml提供跨平台。

编辑:不要误解我:Base64是标准编码算法。对于相同的数据,它给出相同的输出。我是说字节数组可能不同