开发工具分享
  • 首页
  • 计算科学
  • 文化旅游
  • 项目和网站
    • OSSEZ 计算技术
    • USRealEstate 社区
    • 地区文化
    • CWIKI.US
    • BUG.OSSEZ.COM
    • RSS.OSSEZ.COM
CWIKIUS.CN
一个有独立思考和温度的清新站
  1. Home
  2. Computer Science
  3. This article

Java 中将 UUID 存储为 Base64 字符串

2024年04月28日 537Browse 0Like 0Comments

概述

使用 Base64 编码来对 UUID(Universally Unique Identifiers) 存储在一些特定的场合被广泛的使用。使用 Base64 对比直接使用 UUID 进行存储来说能够更多的节约空间。

 

本文对这方面的相关内容和问题进行探讨。

 

在这里,使用 Base64 来对 UUID 进行存储,涉及到一些类型的转换的。Base64 是编码算法,在实际使用的时候我们更多会用到 Byte 数组的方式来进行编码的。这样我们就比较明确在对其进行 Base64 转换之前,我们应该要先干什么了。

使用 byte[] 和 Base64.Encoder

Base64.Encoder 就能够提供 byte[] 的 Base64 编码了,我们先使用这个最简单的方式来进行处理。

编码

首先我们需要给出的 UUID 位中创建出我们需要的 byte 数组。

我们先获得 UUID 的 most significant bits 和 least significant bits,然后放入我们 byte 数组中的 0-7 和 8-15 的位置。

程序代码如下:

    private 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;
    }

上面的代码中还有一个 fillByteArray 方法,这个方法,这个方法将会把我们的 bit 存如 byte array 数组中,同时还会移动 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));
    }
}

当我们获得 byte 数组后,我们就可以调用 JDK 的 Base64.Encoder 方法来直接进行编码了成一个 Base64 加密字符串了。

完整的测试代码如下:

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);
}

解码

把我们获得的 UUID Base64 字符串进行解码,我们可以使用完全相反的方法:

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

首先把 Base64 字符串解码成 Byte 数组,然后调用我们的转换方法,把我们获得 byte 数组转换成为 UUID 对象。

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

    return new UUID(mostSignificantBits, leastSignificantBits);
}

在上面的方法中,我们分别对 UUID 中需要使用的 most significant bits 和 less significant bits 分别进行转换,然后再组合在一起。

转换的方法如下:

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;
}

通过上面的测试代码,可以看到代码的转换都顺利完成了。

使用 ByteBuffer 和 Base64.getUrlEncoder()

如果我们还使用 JDK 的 API 的话,我们还可以把上面的代码进行一些简化。

编码

通过使用 ByteBuffer,我们可以使用非常简单的下面 2 行代码把 UUID 的 bit 转换为 buffer wrapping 数组。

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

当程序执行完上面的 2 行代码后,我们将会获得 一个 ByteBuffer 对象,这个对象中存储的是 UUID 转换过来的数据。

针对编码,我们就可以使用 Base64.getUrlEncoder() 方法,这个方法的参数我们可以使用 ByteBuffer 转换成 array 就可以了,因为 ByteBuffer 转换成 Array 是返回 Array 的。

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

针对上面代码的修改,我们只需要下面简单的几行代码就可以完成 UUID 到 Base64 的转换。

@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);
}

解码

解码就使用 Base64.UrlDecoder() 即可,Base64.UrlDecoder() 的解码结果为 byte 数组,所以我们还需要用 ByteBuffer.wrap 把解码后的数组包装成 ByteBuffer 对象。

@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);
}

缩短编码后字符串

在我们完成上面的编码后,我们会看到字符串的最后还有 2 个等号 “==” 。为了进一步节约我们的存储空间,我们可以把这 2 个等号从字符串中删除。

我们可以配置编码函数,告诉编码函数不要在字符串的末尾添加 2 个等号。

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

assertEquals(expectedEncodedString, encodedUUID);

针对解码的方法来说,我们并不需要对方法进行任何改变,因为现在的解码方法已经能够正确识别 Base64 字符串的末尾是不是有 2 个等号,并且可都兼容。

  1. 使用 Apache Commons Conversion Utils 和Codec Utils 工具类
    在这部分,我们使用 Apache Commons Conversion Utils 的工具类来先把 UUID 对象转换为 UUID byte 数组,然后使用 Apache Commons Codec Utils 工具类来把进行 Base64 的字符串处理。

依赖

为了完成上面 2 个步骤,我们需要分别使用 Apache Commons Lang library 和 commons-codec 类库

在我们的 pom.xml 项目文件中,分别添加上面 2 个类库的依赖。

通常你的项目基本上都会包含进来的,如果没有的话再添加。

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

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

编码

上面的编码就非常简单了,直接使用 Conversion.uuidToByteArray 方法,把要编码的 UUID 对象传进来,然后直接 Base64,使用 URL 安全的方法。

@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);
}

通过上面的代码,我们可以看到结果是 Base 64 已经把最后的 2 个等号删掉了。

解码

针对解码来说,我们使用的是 JDK 的 Base64.decodeBase64() 方法,然后调用 Conversion.byteArrayToUuid() 把解码后的 byte 数组转换为 UUID 对象。

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

结论

UUID 是广泛使用的 ID 识别标识,我们通过对 Base64 的转换来让 UUID 能够以更小的数据量来进行存储。

转换的关键就在于 UUID 先要转换为 byte 数组,然后 Base64 是对 byte[] 数组进行编码的。

 

https://www.isharkfly.com/t/java-uuid-base64/15685

Tags: None
Last updated:2024年04月28日

HoneyMoose

有温度的人文和独立的思考

Like
< Previous
Next >

Comments

Cancel reply

Archives
  • May 2026
  • April 2026
  • March 2026
  • February 2026
  • January 2026
  • December 2025
  • November 2025
  • October 2025
  • September 2025
  • August 2025
  • July 2025
  • June 2025
  • May 2025
  • April 2025
  • March 2025
  • February 2025
  • January 2025
  • December 2024
  • November 2024
  • October 2024
  • September 2024
  • August 2024
  • July 2024
  • June 2024
  • May 2024
  • April 2024
  • March 2024
  • February 2024
  • January 2024
  • December 2023
  • November 2023
  • October 2023
  • September 2023
  • August 2023
  • July 2023
  • June 2023
  • May 2023
  • April 2023
  • December 2022
  • November 2022
  • October 2022
  • September 2022
  • August 2022
  • May 2022
  • April 2022
  • March 2022
  • February 2022
  • January 2022
  • December 2021
  • November 2021
  • October 2021
  • September 2021
  • August 2021
  • July 2021
  • June 2021
  • May 2021
  • April 2021
  • March 2021
  • February 2021
  • January 2021
  • December 2020
  • November 2020
  • October 2020
  • September 2020
  • August 2020
  • July 2020
  • June 2020
  • May 2020
  • April 2020
  • March 2020
  • February 2020
  • January 2020
  • December 2019
  • November 2019
  • October 2019
  • September 2019
  • August 2019
  • July 2019
  • June 2019
  • May 2019
  • April 2019
  • March 2019
  • February 2019
  • January 2019
  • December 2018
  • November 2018
  • October 2018
  • September 2018
  • August 2018
  • July 2018
  • June 2018
  • May 2018
  • April 2018
  • March 2018
Categories
  • Computer Science (2,362)
    • Confluence (663)
    • Gradle (12)
  • U.S. (482)
  • 文化旅游 (145)

COPYRIGHT © 2020 CWIKIUS. ALL RIGHTS RESERVED.

THEME KRATOS MADE BY VTROIS

湘ICP备2020018253号-1