使用 Base64 编码字符串是一种广泛采用的存储通用唯一标识符 (UUID) 的方法。与标准 UUID 字符串表示形式相比,这提供了更紧凑的结果。在本文中,我们将探讨将 UUID 编码为 Base64 字符串的三种方法。
第一种方法:使用byte[]和Base64.Encoder进行编码
我们将从使用byte[]和Base64.Encoder的最直接的编码方法开始。
1、编码
从 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; }
|
在填充方法中,我们将位移动到数组中,将它们转换为字节,并在每次迭代中移动 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)); } }
|
在下一步中,我们将使用 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); }
|
可以看到,得到的值正是我们所期望的。2、解码
要从 Base64 编码的字符串中解码 UUID,我们可以通过以下方式执行相反的操作:
@Test public void givenEncodedString_whenDecodingUsingBase64Decoder_thenGiveExpectedUUID() { String expectedEncodedString = "UUrxjPeTX8xsDDoxQOfGgw=="; byte[] decodedBytes = Base64.getDecoder().decode(expectedEncodedString); UUID uuid = convertToUUID(decodedBytes); }
|
首先,我们使用Base64.Decoder从编码字符串中获取字节数组,并调用转换方法从该数组创建 UUID:UUID convertToUUID(byte[] src) { long mostSignificantBits = convertBytesToLong(src, 0); long leastSignificantBits = convertBytesToLong(src, 8); return new UUID(mostSignificantBits, leastSignificantBits); }
|
我们将数组的一部分转换为最高和最低有效位长度表示形式,并使用它们创建 UUID。转换方法如下:
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; }
|
在此方法中,我们遍历字节数组,将它们每个转换为位,然后将它们移动到我们的结果中。正如我们所看到的,解码的最终结果将与我们用于编码的原始UUID相匹配。
第二种方法:使用ByteBuffer和Base64.getUrlEncoder()进行编码
使用 JDK 的标准功能,我们可以简化上面编写的代码。
1. 编码
使用ByteBuffer,我们只需几行代码即可完成将 UUID 转换为字节数组的过程:
ByteBuffer byteBuffer = ByteBuffer.wrap(new byte[16]); byteBuffer.putLong(originalUUID.getMostSignificantBits()); byteBuffer.putLong(originalUUID.getLeastSignificantBits());
|
我们创建了一个包装字节数组的缓冲区,并放置 UUID 中的最高和最低有效位。
出于编码目的,我们这次将使用Base64.getUrlEncoder() :
String encodedUUID = Base64.getUrlEncoder().encodeToString(byteBuffer.array());
结果,我们用 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); }
|
2. 解码
我们可以使用ByteBuffer和Base64.UrlDecoder()执行相反的操作:
@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); }
|
正如我们所看到的,我们成功地从编码字符串中解码出预期的 UUID。减少编码UUID的长度
正如我们在前面几节中看到的,Base64 默认情况下在末尾包含“==” 。为了节省更多字节,我们可以修剪这个结尾。
为此,我们可以将编码器配置为不添加填充:
String encodedUUID = Base64.getUrlEncoder().withoutPadding().encodeToString(byteBuffer.array()); assertEquals(expectedEncodedString, encodedUUID);
|
结果,我们可以看到编码后的字符串没有多余的字符。无需更改我们的解码器,因为它将以相同的方式处理编码字符串的两种变体。
第三种:使用 Apache Commons 中的转换实用程序和编解码器实用程序进行编码
在本节中,我们将使用Apache Commons Conversion utils中的uuidToByteArray来创建 UUID 字节数组。另外,我们将使用Apache Commons Base64 utils中的encodeBase64URLSafeString。
为了演示这种编码方法,我们将使用 Apache Commons Lang 库。让我们将其依赖项添加到pom.xml中:
<dependency> <groupId>org.apache.commons</groupId> <artifactId>commons-lang3</artifactId> <version>3.14.0</version> </dependency> 我们将使用的另一个依赖项是commons-codec:
<dependency> <groupId>commons-codec</groupId> <artifactId>commons-codec</artifactId> <version>1.16.0</version> </dependency>
|
1. 编码
我们只需两行代码即可对 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); }
|
正如我们所看到的,结果已经被修剪并且不包含待定的结局。2. 解码
我们将从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); }
|
我们成功获得了原始的UUID。