将UUID存储为base64string
我一直在尝试使用UUID作为数据库密钥。 我想占用尽可能less的字节数,同时仍然保持UUID表示人类可读。
我认为我已经得到它使用base64 22字节,并删除一些似乎是不必要的尾随“==”为我的目的存储。 这种方法有什么缺陷吗?
基本上我的testing代码做了一堆转换,把UUID降到22字节的string,然后把它转换回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; } }
输出:
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
您可以安全地删除填充“==”在这个应用程序。 如果你要将base-64文本解码回字节,大多数库会希望它在那里,但是因为你只是使用结果string作为关键字,所以这不是问题。
我喜欢Base-64,因为它的有限的字符集看起来不像乱码,但也有Base-85 。 它使用更多的字符和代码4字节作为5个字符,所以你可以让你的文本下降到20个字符。
我也试图做类似的事情。 我正在使用一个Java应用程序,该应用程序使用forms为6fcb514b-b878-4c9d-95b7-8dc3a7ce6fd8
的UUID(使用Java中的标准UUID库生成)。 在我的情况下,我需要能够将这个UUID降到30个字符或更less。 我使用Base64,这些是我的便利function。 希望他们会对别人有所帮助,因为解决scheme对我来说不是很明显。
用法:
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));
输出:
as base64: b8tRS7h4TJ2Vt43Dp85v2A as uuid : 6fcb514b-b878-4c9d-95b7-8dc3a7ce6fd8
function:
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(); }
我有一个应用程序,我几乎正是这样做。 22个字符编码的UUID。 它工作正常。 不过,我这样做的主要原因是ID在Web应用程序的URI中公开,而对于出现在URI中的事物,36个字符实际上相当大。 22个字还是挺长的,但是我们做了。
这里是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
它与base64编码不完全相同,因为base64使用的字符必须在出现在URIpath组件中时才会被转义。 Java的实现可能会有很大的不同,因为你更可能有一个原始字节数组,而不是一个真正的大整数。
这里是我的代码,它使用org.apache.commons.codec.binary.Base64来生成长度为22个字符(且与UUID具有相同唯一性)的url安全唯一string。
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)); }
你不会说你正在使用什么DBMS,但是如果你关心节省空间的话,RAW似乎是最好的方法。 你只需要记住转换所有的查询,否则你将面临巨大的性能下降。
但是我必须问:你住的地方的字节真的很贵吗?
以下是我用于UUID(梳子风格)。 它包括用于将uuidstring或uuidtypes转换为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; } }