Memorizzare UUID come stringa base64

Ho provato a utilizzare gli UUID come chiavi del database. Voglio prendere la minor quantità ansible di byte, mantenendo comunque la rappresentazione UUID leggibile dall’uomo.

Penso di aver ottenuto fino a 22 byte usando base64 e rimuovendo alcuni “==” finali che sembrano non essere necessari per archiviare per i miei scopi. Ci sono dei difetti con questo approccio?

Fondamentalmente il mio codice di test fa un po ‘di conversioni per portare l’UUID su una stringa di 22 byte, quindi lo converte in un 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 * (7 - i)); } for (int i = 8; i >> 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; } } 

produzione:

 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 

Puoi tranquillamente rilasciare il padding “==” in questa applicazione. Se dovessi decodificare il testo base-64 in byte, la maggior parte delle librerie si aspetterebbe che fosse lì, ma dal momento che stai usando la stringa risultante come chiave, non è un problema.

Mi piace Base-64 perché il suo set di caratteri limitato sembra un po ‘incomprensibile, ma c’è anche Base-85 . Usa più caratteri e codifica 4 byte come 5 caratteri, in modo che il testo possa arrivare a 20 caratteri.

Stavo anche cercando di fare qualcosa di simile. Sto lavorando con un’applicazione Java che utilizza UUID del modulo 6fcb514b-b878-4c9d-95b7-8dc3a7ce6fd8 (che sono generati con la lib UUID standard in Java). Nel mio caso dovevo essere in grado di ottenere questo UUID fino a 30 caratteri o meno. Ho usato Base64 e queste sono le mie funzioni di convenienza. Spero che saranno utili per qualcuno, perché la soluzione non era ovvia per me subito.

Uso:

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

Produzione:

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

funzioni:

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

Ho un’applicazione in cui sto facendo quasi esattamente questo. 22 UUID codificato in caratteri. Funziona bene. Tuttavia, la ragione principale per cui sto facendo in questo modo è che gli ID sono esposti negli URI della web app, e 36 caratteri sono davvero grandi per qualcosa che appare in un URI. 22 caratteri sono ancora un po ‘lunghi, ma ci arrangiamo.

Ecco il codice Ruby per questo:

  # 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 

Non è esattamente la stessa della codifica base64, in quanto base64 utilizza caratteri che dovrebbero essere sfuggiti se compaiono in un componente del percorso URI. L’implementazione di Java è probabilmente molto diversa poiché è più probabile che tu abbia una matrice di byte grezzi invece di un numero intero veramente grande.

Ecco il mio codice, utilizza org.apache.commons.codec.binary.Base64 per produrre stringhe uniche url-safe con una lunghezza di 22 caratteri (e che hanno la stessa univocità di 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)); } 

Non si dice quale DBMS si sta utilizzando, ma sembra che RAW sarebbe l’approccio migliore se si è preoccupati di risparmiare spazio. Devi solo ricordarti di convertire per tutte le query, altrimenti rischierai un enorme calo delle prestazioni.

Ma devo chiedertelo: i byte sono davvero così costosi dove vivi?

Di seguito è quello che uso per un UUID (stile pettine). Include codice per convertire una stringa uuid o un tipo uuid in base64. Lo faccio per 64 bit, quindi non mi occupo di alcun segno di uguale:

GIAVA

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