Java 将 UUID 存储为 base64 字符串

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/772802/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-11 19:18:44  来源:igfitidea点击:

Storing UUID as base64 String

javasqlbytearraybase64uuid

提问by mainstringargs

I have been experimenting with using UUIDs as database keys. I want to take up the least amount of bytes as possible, while still keeping the UUID representation human readable.

我一直在尝试使用 UUID 作为数据库键。我想占用尽可能少的字节,同时仍然保持 UUID 表示人类可读。

I think that I have gotten it down to 22 bytes using base64 and removing some trailing "==" that seem to be unnecessary to store for my purposes. Are there any flaws with this approach?

我认为我已经使用 base64 将其减少到 22 个字节,并删除了一些对于我的目的似乎没有必要存储的尾随“==”。这种方法有什么缺陷吗?

Basically my test code does a bunch of conversions to get the UUID down to a 22 byte String, then converts it back into a UUID.

基本上,我的测试代码进行了一系列转换以将 UUID 降为 22 字节字符串,然后将其转换回 UUID。

import java.io.IOException;
import java.util.UUID;

public class UUIDTest {

    public static void main(String[] args){
        UUID uuid = UUID.randomUUID();
        System.out.println("UUID String: " + uuid.toString());
        System.out.println("Number of Bytes: " + uuid.toString().getBytes().length);
        System.out.println();

        byte[] uuidArr = asByteArray(uuid);
        System.out.print("UUID Byte Array: ");
        for(byte b: uuidArr){
            System.out.print(b +" ");
        }
        System.out.println();
        System.out.println("Number of Bytes: " + uuidArr.length);
        System.out.println();


        try {
            // Convert a byte array to base64 string
            String s = new sun.misc.BASE64Encoder().encode(uuidArr);
            System.out.println("UUID Base64 String: " +s);
            System.out.println("Number of Bytes: " + s.getBytes().length);
            System.out.println();


            String trimmed = s.split("=")[0];
            System.out.println("UUID Base64 String Trimmed: " +trimmed);
            System.out.println("Number of Bytes: " + trimmed.getBytes().length);
            System.out.println();

            // Convert base64 string to a byte array
            byte[] backArr = new sun.misc.BASE64Decoder().decodeBuffer(trimmed);
            System.out.print("Back to UUID Byte Array: ");
            for(byte b: backArr){
                System.out.print(b +" ");
            }
            System.out.println();
            System.out.println("Number of Bytes: " + backArr.length);

            byte[] fixedArr = new byte[16];
            for(int i= 0; i<16; i++){
                fixedArr[i] = backArr[i];
            }
            System.out.println();
            System.out.print("Fixed UUID Byte Array: ");
            for(byte b: fixedArr){
                System.out.print(b +" ");
            }
            System.out.println();
            System.out.println("Number of Bytes: " + fixedArr.length);

            System.out.println();
            UUID newUUID = toUUID(fixedArr);
            System.out.println("UUID String: " + newUUID.toString());
            System.out.println("Number of Bytes: " + newUUID.toString().getBytes().length);
            System.out.println();

            System.out.println("Equal to Start UUID? "+newUUID.equals(uuid));
            if(!newUUID.equals(uuid)){
                System.exit(0);
            }


        } catch (IOException e) {
        }

    }


    public static byte[] asByteArray(UUID uuid) {

        long msb = uuid.getMostSignificantBits();
        long lsb = uuid.getLeastSignificantBits();
        byte[] buffer = new byte[16];

        for (int i = 0; i < 8; i++) {
            buffer[i] = (byte) (msb >>> 8 * (7 - i));
        }
        for (int i = 8; i < 16; i++) {
            buffer[i] = (byte) (lsb >>> 8 * (7 - i));
        }

        return buffer;

    }

    public static UUID toUUID(byte[] byteArray) {

        long msb = 0;
        long lsb = 0;
        for (int i = 0; i < 8; i++)
            msb = (msb << 8) | (byteArray[i] & 0xff);
        for (int i = 8; i < 16; i++)
            lsb = (lsb << 8) | (byteArray[i] & 0xff);
        UUID result = new UUID(msb, lsb);

        return result;
    }

}

output:

输出:

UUID String: cdaed56d-8712-414d-b346-01905d0026fe
Number of Bytes: 36

UUID Byte Array: -51 -82 -43 109 -121 18 65 77 -77 70 1 -112 93 0 38 -2 
Number of Bytes: 16

UUID Base64 String: za7VbYcSQU2zRgGQXQAm/g==
Number of Bytes: 24

UUID Base64 String Trimmed: za7VbYcSQU2zRgGQXQAm/g
Number of Bytes: 22

Back to UUID Byte Array: -51 -82 -43 109 -121 18 65 77 -77 70 1 -112 93 0 38 -2 0 38 
Number of Bytes: 18

Fixed UUID Byte Array: -51 -82 -43 109 -121 18 65 77 -77 70 1 -112 93 0 38 -2 
Number of Bytes: 16

UUID String: cdaed56d-8712-414d-b346-01905d0026fe
Number of Bytes: 36

Equal to Start UUID? true

采纳答案by erickson

You can safely drop the padding "==" in this application. If you were to decode the base-64 text back to bytes, most libraries would expect it to be there, but since you are just using the resulting string as a key, it's not a problem.

您可以安全地删除此应用程序中的填充“==”。如果您要将 base-64 文本解码回字节,大多数库都希望它在那里,但由于您只是将结果字符串用作键,所以这不是问题。

I like Base-64 because its limited character set looks less like gibberish, but there's also Base-85. It uses more characters and codes 4 bytes as 5 characters, so you could get your text down to 20 characters.

我喜欢 Base-64 是因为它有限的字符集看起来不像乱码,但也有Base-85。它使用更多字符并将 4 个字节编码为 5 个字符,因此您可以将文本减少到 20 个字符。

回答by kdgregory

You don't say what DBMS you're using, but it seems that RAW would be the best approach if you're concerned about saving space. You just need to remember to convert for all queries, or you'll risk a huge performance drop.

您没有说明您使用的是什么 DBMS,但如果您担心节省空间,RAW 似乎是最好的方法。您只需要记住为所有查询进行转换,否则您将面临性能大幅下降的风险。

But I have to ask: are bytes really that expensive where you live?

但我不得不问:在你住的地方,字节真的那么贵吗?

回答by Bob Aman

I have an application where I'm doing almost exactly this. 22 char encoded UUID. It works fine. However, the main reason I'm doing it this way is that the IDs are exposed in the web app's URIs, and 36 characters is really quite big for something that appears in a URI. 22 characters is still kinda long, but we make do.

我有一个应用程序,我几乎就是这样做的。22 字符编码的 UUID。它工作正常。但是,我这样做的主要原因是 ID 在 Web 应用程序的 URI 中公开,而 36 个字符对于出现在 URI 中的内容来说确实非常大。22 个字符还是有点长,但我们凑合。

Here's the Ruby code for this:

这是用于此的 Ruby 代码:

  # Make an array of 64 URL-safe characters
  CHARS64 = ("a".."z").to_a + ("A".."Z").to_a + ("0".."9").to_a + ["-", "_"]
  # Return a 22 byte URL-safe string, encoded six bits at a time using 64 characters
  def to_s22
    integer = self.to_i # UUID as a raw integer
    rval = ""
    22.times do
      c = (integer & 0x3F)
      rval += CHARS64[c]
      integer = integer >> 6
    end
    return rval.reverse
  end

It's not exactly the same as base64 encoding because base64 uses characters that would have to be escaped if they appeared in a URI path component. The Java implementation is likely to be quite different since you're more likely to have an array of raw bytes instead of a really big integer.

它与 base64 编码并不完全相同,因为 base64 使用的字符如果出现在 URI 路径组件中就必须进行转义。Java 实现可能完全不同,因为您更有可能拥有原始字节数组而不是真正的大整数。

回答by Dennis

Below is what I use for a UUID (Comb style). It includes code for converting a uuid string or uuid type to base64. I do it per 64 bits, so I don't deal with any equal signs:

下面是我用于 UUID(梳状样式)的内容。它包括将 uuid 字符串或 uuid 类型转换为 base64 的代码。我是按 64 位执行的,所以我不处理任何等号:

JAVA

爪哇

import java.util.Calendar;
import java.util.UUID;
import org.apache.commons.codec.binary.Base64;

public class UUIDUtil{
    public static UUID combUUID(){
        private UUID srcUUID = UUID.randomUUID();
        private java.sql.Timestamp ts = new java.sql.Timestamp(Calendar.getInstance().getTime().getTime());

        long upper16OfLowerUUID = this.zeroLower48BitsOfLong( srcUUID.getLeastSignificantBits() );
        long lower48Time = UUIDUtil.zeroUpper16BitsOfLong( ts );
        long lowerLongForNewUUID = upper16OfLowerUUID | lower48Time;
        return new UUID( srcUUID.getMostSignificantBits(), lowerLongForNewUUID );
    }   
    public static base64URLSafeOfUUIDObject( UUID uuid ){
        byte[] bytes = ByteBuffer.allocate(16).putLong(0, uuid.getLeastSignificantBits()).putLong(8, uuid.getMostSignificantBits()).array();
        return Base64.encodeBase64URLSafeString( bytes );
    }
    public static base64URLSafeOfUUIDString( String uuidString ){
    UUID uuid = UUID.fromString( uuidString );
        return UUIDUtil.base64URLSafeOfUUIDObject( uuid );
    }
    private static long zeroLower48BitsOfLong( long longVar ){
        long upper16BitMask =  -281474976710656L;
        return longVar & upper16BitMask;
    }
    private static void zeroUpper16BitsOfLong( long longVar ){
        long lower48BitMask =  281474976710656L-1L;
        return longVar & lower48BitMask;
    }
}

回答by swill

I was also trying to do something similar. I am working with a Java application which uses UUIDs of the form 6fcb514b-b878-4c9d-95b7-8dc3a7ce6fd8(which are generated with the standard UUID lib in Java). In my case I needed to be able to get this UUID down to 30 characters or less. I used Base64 and these are my convenience functions. Hopefully they will be helpful for someone as the solution was not obvious to me right away.

我也试图做类似的事情。我正在使用一个 Java 应用程序,该应用程序使用表单的 UUID 6fcb514b-b878-4c9d-95b7-8dc3a7ce6fd8(使用 Java 中的标准 UUID 库生成)。就我而言,我需要能够将这个 UUID 减少到 30 个字符或更少。我使用了 Base64,这些是我的便利功能。希望他们会对某人有所帮助,因为解决方案对我来说并不明显。

Usage:

用法:

String uuid_str = "6fcb514b-b878-4c9d-95b7-8dc3a7ce6fd8";
String uuid_as_64 = uuidToBase64(uuid_str);
System.out.println("as base64: "+uuid_as_64);
System.out.println("as uuid: "+uuidFromBase64(uuid_as_64));

Output:

输出:

as base64: b8tRS7h4TJ2Vt43Dp85v2A
as uuid  : 6fcb514b-b878-4c9d-95b7-8dc3a7ce6fd8

Functions:

职能:

import org.apache.commons.codec.binary.Base64;

private static String uuidToBase64(String str) {
    Base64 base64 = new Base64();
    UUID uuid = UUID.fromString(str);
    ByteBuffer bb = ByteBuffer.wrap(new byte[16]);
    bb.putLong(uuid.getMostSignificantBits());
    bb.putLong(uuid.getLeastSignificantBits());
    return base64.encodeBase64URLSafeString(bb.array());
}
private static String uuidFromBase64(String str) {
    Base64 base64 = new Base64(); 
    byte[] bytes = base64.decodeBase64(str);
    ByteBuffer bb = ByteBuffer.wrap(bytes);
    UUID uuid = new UUID(bb.getLong(), bb.getLong());
    return uuid.toString();
}

回答by stikkos

Here's my code, it uses org.apache.commons.codec.binary.Base64 to produce url-safe unique strings that are 22 characters in length (and that have the same uniqueness as UUID).

这是我的代码,它使用 org.apache.commons.codec.binary.Base64 生成长度为 22 个字符的 url-safe 唯一字符串(并且与 UUID 具有相同的唯一性)。

private static Base64 BASE64 = new Base64(true);
public static String generateKey(){
    UUID uuid = UUID.randomUUID();
    byte[] uuidArray = KeyGenerator.toByteArray(uuid);
    byte[] encodedArray = BASE64.encode(uuidArray);
    String returnValue = new String(encodedArray);
    returnValue = StringUtils.removeEnd(returnValue, "\r\n");
    return returnValue;
}
public static UUID convertKey(String key){
    UUID returnValue = null;
    if(StringUtils.isNotBlank(key)){
        // Convert base64 string to a byte array
        byte[] decodedArray = BASE64.decode(key);
        returnValue = KeyGenerator.fromByteArray(decodedArray);
    }
    return returnValue;
}
private static byte[] toByteArray(UUID uuid) {
    byte[] byteArray = new byte[(Long.SIZE / Byte.SIZE) * 2];
    ByteBuffer buffer = ByteBuffer.wrap(byteArray);
    LongBuffer longBuffer = buffer.asLongBuffer();
    longBuffer.put(new long[] { uuid.getMostSignificantBits(), uuid.getLeastSignificantBits() });
    return byteArray;
}
private static UUID fromByteArray(byte[] bytes) {
    ByteBuffer buffer = ByteBuffer.wrap(bytes);
    LongBuffer longBuffer = buffer.asLongBuffer();
    return new UUID(longBuffer.get(0), longBuffer.get(1));
}

回答by Sergey Ponomarev

Here is an example with java.util.Base64introduced in JDK8:

这是java.util.Base64JDK8中引入的示例:

import java.nio.ByteBuffer;
import java.util.Base64;
import java.util.Base64.Encoder;
import java.util.UUID;

public class Uuid64 {

  private static final Encoder BASE64_URL_ENCODER = Base64.getUrlEncoder().withoutPadding();

  public static void main(String[] args) {
    // String uuidStr = UUID.randomUUID().toString();
    String uuidStr = "eb55c9cc-1fc1-43da-9adb-d9c66bb259ad";
    String uuid64 = uuidHexToUuid64(uuidStr);
    System.out.println(uuid64); //=> 61XJzB_BQ9qa29nGa7JZrQ
    System.out.println(uuid64.length()); //=> 22
    String uuidHex = uuid64ToUuidHex(uuid64);
    System.out.println(uuidHex); //=> eb55c9cc-1fc1-43da-9adb-d9c66bb259ad
  }

  public static String uuidHexToUuid64(String uuidStr) {
    UUID uuid = UUID.fromString(uuidStr);
    byte[] bytes = uuidToBytes(uuid);
    return BASE64_URL_ENCODER.encodeToString(bytes);
  }

  public static String uuid64ToUuidHex(String uuid64) {
    byte[] decoded = Base64.getUrlDecoder().decode(uuid64);
    UUID uuid = uuidFromBytes(decoded);
    return uuid.toString();
  }

  public static byte[] uuidToBytes(UUID uuid) {
    ByteBuffer bb = ByteBuffer.wrap(new byte[16]);
    bb.putLong(uuid.getMostSignificantBits());
    bb.putLong(uuid.getLeastSignificantBits());
    return bb.array();
  }

  public static UUID uuidFromBytes(byte[] decoded) {
    ByteBuffer bb = ByteBuffer.wrap(decoded);
    long mostSigBits = bb.getLong();
    long leastSigBits = bb.getLong();
    return new UUID(mostSigBits, leastSigBits);
  }
}

The UUID encoded in Base64 is URL safe and without padding.

以 Base64 编码的 UUID 是 URL 安全的且没有填充。

回答by Jan Rychter

This is not exactly what you asked for (it isn't Base64), but worth looking at, because of added flexibility: there is a Clojure library that implements a compact 26-char URL-safe representation of UUIDs (https://github.com/tonsky/compact-uuids).

这不是您所要求的(它不是 Base64),但值得一看,因为它增加了灵活性:有一个 Clojure 库,它实现了 UUID 的紧凑的 26 字符 URL 安全表示(https://github .com/tonsky/compact-uuids)。

Some highlights:

一些亮点:

  • Produces strings that are 30% smaller (26 chars vs traditional 36 chars)
  • Supports full UUID range (128 bits)
  • Encoding-safe (uses only readable characters from ASCII)
  • URL/file-name safe
  • Lowercase/uppercase safe
  • Avoids ambiguous characters (i/I/l/L/1/O/o/0)
  • Alphabetical sort on encoded 26-char strings matches default UUID sort order
  • 生成小 30% 的字符串(26 个字符与传统的 36 个字符)
  • 支持完整的 UUID 范围(128 位)
  • 编码安全(仅使用 ASCII 中的可读字符)
  • URL/文件名安全
  • 小写/大写安全
  • 避免歧义字符 (i/I/l/L/1/O/o/0)
  • 编码的 26 字符字符串的字母排序匹配默认的 UUID 排序顺序

These are rather nice properties. I've been using this encoding in my applications both for database keys and for user-visible identifiers, and it works very well.

这些都是相当不错的属性。我一直在我的应用程序中将这种编码用于数据库键和用户可见的标识符,并且效果很好。