存储UUID为base64字符串
题
我一直在尝试使用UUID作为数据库密钥。我想占用字节尽可能最少量的,同时仍保持UUID表示人类可读的。
我认为,我已经得到它下降到使用Base64和除去一些尾随“==”,似乎是不必要的存储我的目的22个字节。是否有使用这种方法的任何缺陷?
基本上我的测试代码并一串转换来获得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;
}
}
输出:
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
解决方案
您可以安全的将在该应用中填充“==”。如果你是基64文本解码回字节,大多数图书馆希望它在那里,但因为你只是使用生成的字符串作为重点,这不是一个问题。
我喜欢的Base-64,因为它的有限字符集看起来不像乱码,但也有碱基85个。它使用更多的字符和代码4个字节为5个字符,所以你可以让你的文字到20个字符。
其他提示
我也试图做同样的事情。我与它采用的形式6fcb514b-b878-4c9d-95b7-8dc3a7ce6fd8
(这是在Java标准的UUID LIB生成)的UUID的Java应用程序的工作。在我来说,我需要的是能够降低得到这个UUID到30个字符或更少。我用的Base64,这些都是我的便利功能。希望他们将是有用的人作为解决方案并不明显,我的时候了。
<强>用法:强>
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
<强>功能强>
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的暴露,以及36个字符是出现在一个URI的东西真的蛮大的。 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,将有,如果他们出现在一个URI路径组件进行转义字符它不完全一样的base64编码。 Java的实现可能是完全不同的,因为你很可能有原始字节,而不是一个真正的大整数数组。
下面是我的代码,它使用org.apache.commons.codec.binary.Base64产生网址安全唯一的字符串,其长度为22个字符(和具有的独特性为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));
}
你不说你正在使用的数据库管理系统,但似乎RAW将是最好的方法,如果你关心节省空间。你只需要记住所有查询转换,否则你会冒一个巨大的性能下降。
但我要问:是真的字节昂贵你住的地方
下面是与JDK8引入java.util.Base64
的示例:
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);
}
}
在Base64编码UUID是URL安全,无填充。
下面是我用的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;
}
}