Storing UUID as Base64 String in Java – 用 Java 将 UUID 存储为 Base64 字符串

最后修改: 2024年 2月 6日

中文/混合/英文(键盘快捷键:t)

1. Overview

1.概述

Using a Base64 encoded string is a widely adopted method for storing Universally Unique Identifiers (UUIDs). This provides a more compact result compared to the standard UUID string representation. In this article, we’ll explore various approaches for encoding UUIDs as Base64 strings.

使用 Base64 编码字符串是一种广泛采用的通用唯一标识符(UUID)存储方法。与标准 UUID 字符串表示法相比,这种方法提供了更紧凑的结果。在本文中,我们将探讨将 UUID 编码为 Base64 字符串的各种方法

2.  Encode using byte[] and Base64.Encoder

2. 使用 byte[]Base64.Encoder 进行编码

We’ll start with the most straightforward approach to encoding by using byte[] and Base64.Encoder.

我们将从最直接的编码方法开始,使用 byte[]Base64.Encoder 进行编码。

2.1. Encoding

2.1 编码

We’ll create an array of bytes from our UUID bits. For this purpose, we’ll take the most significant bits and least significant bits from our UUID and place them in our array at positions 0-7 and 8-15, respectively:

我们将用 UUID 位创建一个字节数组。为此,我们将从 UUID 中提取最有效位和最小有效位,并将它们分别放在数组的 0-7 和 8-15 位

byte[] convertToByteArray(UUID uuid) {
    byte[] result = new byte[16];

    long mostSignificantBits = uuid.getMostSignificantBits();
    fillByteArray(0, 8, result, mostSignificantBits);

    long leastSignificantBits = uuid.getLeastSignificantBits();
    fillByteArray(8, 16, result, leastSignificantBits);

    return result;
}

In the filling method, we move bits to our array, converting them into bytes and shifting by 8 bits in each iteration:

在填充方法中,我们将比特移动到数组中,将其转换为字节,并在每次迭代中移动 8 比特:

void fillByteArray(int start, int end, byte[] result, long bits) {
    for (int i = start; i < end; i++) {
        int shift = i * 8;
        result[i] = (byte) ((int) (255L & bits >> shift));
    }
}

In the next step, we’ll Base64.Encoder from JDK to encode our byte array into a string:

下一步,我们将使用 JDK 中的 Base64.Encoder 将字节数组编码为字符串:

UUID originalUUID = UUID.fromString("cc5f93f7-8cf1-4a51-83c6-e740313a0c6c");

@Test
void givenEncodedString_whenDecodingUsingBase64Decoder_thenGiveExpectedUUID() {
    String expectedEncodedString = "UUrxjPeTX8xsDDoxQOfGgw==";
    byte[] uuidBytes = convertToByteArray(originalUUID);
    String encodedUUID = Base64.getEncoder().encodeToString(uuidBytes);
    assertEquals(expectedEncodedString, encodedUUID);
}

As we can see, the obtained value is exactly what we expected.

我们可以看到,得到的数值与我们的预期完全一致。

2.2. Decoding

2.2.解码

To decode a UUID from a Base64 encoded string, we can perform the opposite actions in the following manner:

要从 Base64 编码字符串中解码 UUID,我们可以按以下方式执行相反的操作:

@Test
public void givenEncodedString_whenDecodingUsingBase64Decoder_thenGiveExpectedUUID() {
    String expectedEncodedString = "UUrxjPeTX8xsDDoxQOfGgw==";
    byte[] decodedBytes = Base64.getDecoder().decode(expectedEncodedString);
    UUID uuid = convertToUUID(decodedBytes);
}

Firstly, we used Base64.Decoder to obtain a byte array from our encoded string and call our conversion method to make a UUID from this array:

首先,我们使用Base64.Decoder从编码字符串中获取字节数组,然后调用转换方法从数组中生成 UUID:

UUID convertToUUID(byte[] src) {
    long mostSignificantBits = convertBytesToLong(src, 0);
    long leastSignificantBits = convertBytesToLong(src, 8);

    return new UUID(mostSignificantBits, leastSignificantBits);
}

We convert parts of our array to the most and least significant bits long representation and make UUID using them.

我们将数组的部分内容转换为最多和最少有效位的长表示法,并使用它们制作 UUID。

The conversion method is following:

转换方法如下:

long convertBytesToLong(byte[] uuidBytes, int start) {
    long result = 0;

    for(int i = 0; i < 8; i++) {
        int shift = i * 8;
        long bits = (255L & (long)uuidBytes[i + start]) << shift;
        long mask = 255L << shift;
        result = result & ~mask | bits;
    }

    return result;
}

In this method, we go through the bytes array, convert each of them to bits, and move them into our result.

在此方法中,我们会遍历字节数组,将每个字节转换为比特,然后将它们移入我们的结果中。

As we can see, the final result of the decoding will match the original UUID we used for encoding.

我们可以看到,解码的最终结果将与我们用于编码的原始 UUID 一致

3. Encode using ByteBuffer and Base64.getUrlEncoder()

3. 使用 ByteBufferBase64.getUrlEncoder() 进行编码

Using the standard functionality from JDK, we can simplify the code written above.

利用 JDK 的标准功能,我们可以简化上面编写的代码。

3.1. Encoding

3.1 编码

Using a ByteBuffer, we can make the process of transforming our UUID into a byte array in just a few lines of code:

使用 ByteBuffer ,我们只需几行代码就能完成将 UUID 转换为字节数组的过程:

ByteBuffer byteBuffer = ByteBuffer.wrap(new byte[16]);
byteBuffer.putLong(originalUUID.getMostSignificantBits());
byteBuffer.putLong(originalUUID.getLeastSignificantBits());

We created a buffer wrapping a byte array and put the most and least significant bits from our UUID.

我们创建了一个封装字节数组的缓冲区,并将 UUID 的最多和最少有效位放入其中。

For encoding purposes, we’ll use Base64.getUrlEncoder() this time:

为了进行编码,我们这次将使用 Base64.getUrlEncoder()

String encodedUUID = Base64.getUrlEncoder().encodeToString(byteBuffer.array());

As a result, we created a Base64-encoded UUID in 4 lines of code:

因此,我们只用了 4 行代码就创建了 Base64 编码的 UUID:

@Test
public void givenUUID_whenEncodingUsingByteBufferAndBase64UrlEncoder_thenGiveExpectedEncodedString() {
    String expectedEncodedString = "zF-T94zxSlGDxudAMToMbA==";
    ByteBuffer byteBuffer = ByteBuffer.wrap(new byte[16]);
    byteBuffer.putLong(originalUUID.getMostSignificantBits());
    byteBuffer.putLong(originalUUID.getLeastSignificantBits());
    String encodedUUID = Base64.getUrlEncoder().encodeToString(byteBuffer.array());
    assertEquals(expectedEncodedString, encodedUUID);
}

3.2. Decoding

3.2.解码

We can perform the opposite operation using ByteBuffer and Base64.UrlDecoder():

我们可以使用 ByteBufferBase64.UrlDecoder() 执行相反的操作:</em

@Test
void givenEncodedString_whenDecodingUsingByteBufferAndBase64UrlDecoder_thenGiveExpectedUUID() {
    String expectedEncodedString = "zF-T94zxSlGDxudAMToMbA==";
    byte[] decodedBytes = Base64.getUrlDecoder().decode(expectedEncodedString);
    ByteBuffer byteBuffer = ByteBuffer.wrap(decodedBytes);
    long mostSignificantBits = byteBuffer.getLong();
    long leastSignificantBits = byteBuffer.getLong();
    UUID uuid = new UUID(mostSignificantBits, leastSignificantBits);
    assertEquals(originalUUID, uuid);
}

As we can see, we successfully decoded the expected UUID from the encoded string.

我们可以看到,我们成功地从编码字符串中解码出了预期的 UUID。

4. Reduce the Length of an Encoded UUID

4. 减少编码 UUID 的长度 5.

As we saw in previous sections, Base64, by default, contains “==” on the end. To save a few more bytes, we can trim this ending.
For this purpose, we can configure our encoder to not add the padding:

正如我们在前面的章节中所看到的,Base64 默认在结尾包含 “==” 。为了多节省几个字节,我们可以修剪这个结尾。
为此,我们可以配置编码器不添加填充:

String encodedUUID = 
  Base64.getUrlEncoder().withoutPadding().encodeToString(byteBuffer.array());

assertEquals(expectedEncodedString, encodedUUID);

As a result, we can see the encoded string without extra characters. There’s no need to change our decoder since it will work with both variants of the encoded string in the same way.

因此,我们可以看到没有额外字符的编码字符串。无需更改我们的解码器,因为它将以同样的方式处理编码字符串的两种变体

5. Encode Using Conversion Utils and Codec Utils From Apache Commons

5.使用 Apache Commons 中的转换实用程序和编解码器实用程序进行编码

In this section, we’ll use uuidToByteArray from Apache Commons Conversion utils to make an array of UUID bytes. Also, we’ll use encodeBase64URLSafeString from Apache Commons Base64 utils.

在本节中,我们将使用 Apache Commons Conversion 工具中的 uuidToByteArray 来制作 UUID 字节数组。此外,我们还将使用 Apache Commons Base64 工具中的 encodeBase64URLSafeString

5.1. Dependencies

5.1.依赖关系

To demonstrate this encoding approach, we’ll use the Apache Commons Lang library. Let’s add its dependency to our pom.xml:

为了演示这种编码方法,我们将使用 Apache Commons Lang 库。让我们将其 依赖关系添加到 pom.xml 中:

<dependency>
    <groupId>org.apache.commons</groupId>
    <artifactId>commons-lang3</artifactId>
    <version>3.14.0</version>
</dependency>

Another dependency we’ll use is a commons-codec:

我们将使用的另一个 依赖项commons-codec

<dependency>
    <groupId>commons-codec</groupId>
    <artifactId>commons-codec</artifactId>
    <version>1.16.0</version>
</dependency>

5.2. Encoding

5.2.编码

We’ll encode the UUID in just two lines of code:

我们只需两行代码就能对 UUID 进行编码:

@Test
void givenUUID_whenEncodingUsingApacheUtils_thenGiveExpectedEncodedString() {
    String expectedEncodedString = "UUrxjPeTX8xsDDoxQOfGgw";
    byte[] bytes = Conversion.uuidToByteArray(originalUUID, new byte[16], 0, 16);
    String encodedUUID = encodeBase64URLSafeString(bytes);
    assertEquals(expectedEncodedString, encodedUUID);
}

As we can see, the result is already trimmed and doesn’t contain a pending ending.

我们可以看到,结果已经被修剪过,不包含待处理的结尾。

5.3. Decoding

5.3.解码

We’ll make a reverse operation calling Base64.decodeBase64() and Conversion.byteArrayToUuid() from Apache Commons:

我们将调用 Apache Commons 的 Base64.decodeBase64()Conversion.byteArrayToUuid() 进行反向操作:

@Test
void givenEncodedString_whenDecodingUsingApacheUtils_thenGiveExpectedUUID() {
    String expectedEncodedString = "UUrxjPeTX8xsDDoxQOfGgw";
    byte[] decodedBytes = decodeBase64(expectedEncodedString);
    UUID uuid = Conversion.byteArrayToUuid(decodedBytes, 0);
    assertEquals(originalUUID, uuid);
}

We successfully obtained the original UUID.

我们成功获取了原始 UUID。

6. Conclusion

6.结论

UUID is a widely used data type, and one of the approaches to encode it is by using Base64. In this article, we explored a few methods to encode UUID into Base64.

UUID 是一种广泛使用的数据类型,使用 Base64 是对其进行编码的方法之一。在本文中,我们探讨了将 UUID 编码为 Base64 的几种方法。

As usual, the full source code can be found over on GitHub.

像往常一样,完整的源代码可以在 GitHub 上找到