Java 中将 UUID 存储为 Base64 字符串

概述

使用 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 个等号,并且可都兼容。

使用 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[] 数组进行编码的。

Java 中将 UUID 存储为 Base64 字符串 - Java - iSharkFly概述使用 Base64 编码来对 UUID(Universally Unique Identifiers) 存储在一些特定的场合被广泛的使用。使用 Base64 对比直接使用 UUID 进行存储来说能够更多的节约空间。 本文对这方面的相关内容和问题进行探讨。 在这里,使用 Base64 来对 UUID 进行存储,涉及到一些类型的转换的。Base64 是编码算法,在实际使用的时候我们更多会用到 Byte 数组的方式来进行编码的…icon-default.png?t=N7T8https://www.isharkfly.com/t/java-uuid-base64/15685

  • 39
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

HoneyMoose

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值