在 Java 中將 UUID 儲存為 Base64 字串
1. 概述
使用 Base64 編碼字串是一種廣泛採用的儲存通用唯一識別碼 (UUID) 的方法。與標準 UUID 字串表示形式相比,這提供了更緊湊的結果。在本文中,我們將探討將 UUID 編碼為 Base64 字串的各種方法。
2.使用byte[]
和Base64.Encoder
進行編碼
我們將從使用byte[]
和Base64.Encoder
的最直接的編碼方法開始。
2.1.編碼
我們將從 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;
}
在填充方法中,我們將位元移到數組中,將它們轉換為字節,並在每次迭代中移動 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.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相符。
3.使用ByteBuffer
和Base64.getUrlEncoder()
進行編碼
使用 JDK 的標準功能,我們可以簡化上面寫的程式碼。
3.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);
}
3.2.解碼
我們可以使用ByteBuffer
和[Base64.UrlDecoder()](https://docs.oracle.com/en/java/javase/21/docs//api/java.base/java/util/Base64.html#getUrlDecoder()) :
@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。
4. 減少編碼UUID的長度
正如我們在前面幾節中看到的,Base64 預設在末尾包含“==”
。為了節省更多字節,我們可以修剪這個結尾。
為此,我們可以將編碼器配置為不添加填充:
String encodedUUID =
Base64.getUrlEncoder().withoutPadding().encodeToString(byteBuffer.array());
assertEquals(expectedEncodedString, encodedUUID);
結果,我們可以看到編碼後的字串沒有多餘的字元。無需更改我們的解碼器,因為它將以相同的方式處理編碼字串的兩種變體。
5. 使用 Apache Commons 中的轉換實用程式和編解碼器實用程式進行編碼
在本節中,我們將使用Apache Commons Conversion utils 中的uuidToByteArray
來建立 UUID 位元組陣列。另外,我們將使用Apache Commons Base64 utils 中的encodeBase64URLSafeString
。
5.1.依賴關係
為了示範這種編碼方法,我們將使用 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>
5.2.編碼
我們只需兩行程式碼即可對 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);
}
正如我們所看到的,結果已經被修剪並且不包含待定的結局。
5.3.解碼
我們將從 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。
六,結論
UUID 是一種廣泛使用的資料類型,對其進行編碼的方法之一是使用 Base64。在本文中,我們探討了將 UUID 編碼為 Base64 的幾種方法。
像往常一樣,完整的源代碼可以在 GitHub 上找到。