有一些文档介绍如何将 ULID 转换为 UUID,但当您需要将 UUID 转换为 ULID 时却没有那么多文档。
我正在查看这个 UUID / ULID 生成器/转换器 https://www.ulidtools.com/
但我不太确定如何将 UUID 复制到 ULID 转换,源代码太模糊了,我无法理解。
我不知道从哪里开始,这种转换安全吗?它能保证唯一的转换吗?
我也遇到了同样的问题,我查看了 ulidtools.com,然后在深入研究其源代码一段时间后,我发现它在所见的后面使用了 this package。
import pkg from "id128";
const { Ulid, Uuid4 } = pkg;
const ulid = Ulid.generate();
const ulidToUuid = Uuid4.fromRaw(ulid.toRaw());
const uuidToUlid = Ulid.fromRaw(ulidToUuid.toRaw());
console.table([
{
generated_ulid: ulid.toCanonical(),
converted_to_uuid: ulidToUuid.toCanonical(),
converted_back_to_ulid: uuidToUlid.toCanonical(),
},
]);
根据@ImanHosseiniPour 的回答,我得出以下结论:
时间戳 + UUID = ULID
import { Ulid, Uuid4 } from "id128";
import { factory, decodeTime } from 'ulid'
const genUlid = factory();
function convertUuidToUlid(
timestamp: Date,
canonicalUuid: string,
): Ulid {
const uuid = Uuid4.fromCanonical(canonicalUuid);
const convertedUlid = Ulid.fromRaw(uuid.toRaw())
const ulidTimestamp = genUlid(timestamp.valueOf()).slice(0, 10)
const ulidRandom = convertedUlid.toCanonical().slice(10);
return Ulid.fromCanonical(ulidTimestamp + ulidRandom)
}
const timestamp = new Date()
const uuid = 'f0df59ea-bfe2-43a8-98d4-8213348daeb6'
const ulid = convertUuidToUlid(timestamp, uuid)
const originalUuid = Uuid4.fromRaw(ulid.toRaw());
console.table({
timestamp: timestamp.valueOf(),
uuid,
ulid: ulid.toCanonical(),
decodedTime: decodeTime(ulid.toCanonical()),
originalUuid: originalUuid.toCanonical(),
});
请记住,由于我们合并了时间戳,将 ULID 逆向工程为 UUID 将使前两个块与原始块不同
纯Javascript转换方法:
将 UUID 转换为 ULID 可保证代码的唯一性。 ULID 是基于当前时间戳的组合生成的唯一 128 位标识符。
function convertUUIDtoULID(uuid) {
const uuidBinary = uuid.split("-").map((hex) => parseInt(hex, 16));
return uuidBinary
.slice(0, 8)
.map((byte) => byte.toString(32))
.concat(uuidBinary.slice(8).map((byte) => byte.toString(32)))
.join("");
}
const uuid = "454391df-b950-42ea-a2c0-92d62c215d67";
const ulid = convertUUIDtoULID(uuid);
console.log(ulid);
您还可以使用[https://www.npmjs.com/package/ulid]将UUID转换为ULID
这个问题的其他答案没有满足我的需求(没有外部依赖)。这是一个适用于 vanilla ECMAScript2018 JS 的版本:
/**
* Decodes a hexadecimal string (case-insensitive) into an equivalent Uint8Array.
*
* @param {string} hexString The string to decode
* @returns {Uint8Array} The string decoded into binary
*/
function decodeHex(hexString) {
if (typeof hexString !== 'string' || hexString.length % 2 !== 0) {
throw new Error('Invalid hex string');
}
const decoded = new Uint8Array(hexString.length / 2);
for (let i = 0; i < hexString.length; i += 2) {
const byte = parseInt(hexString.substring(i, i + 2), 16);
decoded[i / 2] = byte;
}
return decoded;
}
/**
* The ULID encoding lookup. Notably excludes I, L, U, and O.
*/
const ULID_ENCODING = '0123456789ABCDEFGHJKMNPQRSTVWXYZ';
/**
* Converts a UUID to an equivalent ULID.
*
* @param {string} uuid The UUID, encoded as a 36-character hex-with-dashes string.
* @returns {string} The equivalent ULID, encoded as a 26-character base32 string.
*/
function uuidToUlid(uuid) {
if (!/^[0-9A-Fa-f]{8}(-[0-9A-Fa-f]{4}){3}-[0-9A-Fa-f]{12}$/i.test(uuid)) {
throw new Error('Invalid UUID.');
}
// Break into sections, excluding dashes.
// Using 0179e73f-ff38-a5e0-e633-48fae1c0bd35 as example...
const section1 = uuid.substring(0, 8); // is 0179e73f
const section2 = uuid.substring(9, 13); // is ff38
const section3 = uuid.substring(14, 18); // is a5e0
const section4 = uuid.substring(19, 23); // is e633
const section5 = uuid.substring(24); // is 48fae1c0bd35
// concatenate the parts, decoded into Uint8Array types. This will have length 16.
const decodedArray = [
...decodeHex(section1),
...decodeHex(section2),
...decodeHex(section3),
...decodeHex(section4),
...decodeHex(section5),
];
// optimized unrolled loop for converting 16 bytes into 26 characters, using
// the ULID lookup to translate from integers [0-25] to valid ULID characters.
// ref. https://github.com/RobThree/NUlid
const ulid = [
ULID_ENCODING[(decodedArray[0] & 224) >> 5],
ULID_ENCODING[decodedArray[0] & 31],
ULID_ENCODING[(decodedArray[1] & 248) >> 3],
ULID_ENCODING[((decodedArray[1] & 7) << 2) | ((decodedArray[2] & 192) >> 6)],
ULID_ENCODING[(decodedArray[2] & 62) >> 1],
ULID_ENCODING[((decodedArray[2] & 1) << 4) | ((decodedArray[3] & 240) >> 4)],
ULID_ENCODING[((decodedArray[3] & 15) << 1) | ((decodedArray[4] & 128) >> 7)],
ULID_ENCODING[(decodedArray[4] & 124) >> 2],
ULID_ENCODING[((decodedArray[4] & 3) << 3) | ((decodedArray[5] & 224) >> 5)],
ULID_ENCODING[decodedArray[5] & 31],
ULID_ENCODING[(decodedArray[6] & 248) >> 3],
ULID_ENCODING[((decodedArray[6] & 7) << 2) | ((decodedArray[7] & 192) >> 6)],
ULID_ENCODING[(decodedArray[7] & 62) >> 1],
ULID_ENCODING[((decodedArray[7] & 1) << 4) | ((decodedArray[8] & 240) >> 4)],
ULID_ENCODING[((decodedArray[8] & 15) << 1) | ((decodedArray[9] & 128) >> 7)],
ULID_ENCODING[(decodedArray[9] & 124) >> 2],
ULID_ENCODING[((decodedArray[9] & 3) << 3) | ((decodedArray[10] & 224) >> 5)],
ULID_ENCODING[decodedArray[10] & 31],
ULID_ENCODING[(decodedArray[11] & 248) >> 3],
ULID_ENCODING[((decodedArray[11] & 7) << 2) | ((decodedArray[12] & 192) >> 6)],
ULID_ENCODING[(decodedArray[12] & 62) >> 1],
ULID_ENCODING[((decodedArray[12] & 1) << 4) | ((decodedArray[13] & 240) >> 4)],
ULID_ENCODING[((decodedArray[13] & 15) << 1) | ((decodedArray[14] & 128) >> 7)],
ULID_ENCODING[(decodedArray[14] & 124) >> 2],
ULID_ENCODING[((decodedArray[14] & 3) << 3) | ((decodedArray[15] & 224) >> 5)],
ULID_ENCODING[decodedArray[15] & 31]
].join('');
return ulid;
}
const uuid = '0179e73f-ff38-a5e0-e633-48fae1c0bd35';
const ulid = uuidToUlid(uuid);
console.log(ulid); // 01F7KKZZSRMQGECCT8ZBGW1F9N
如果您想验证功能,这里有一个 JSFiddle。