|
@@ -0,0 +1,156 @@
|
|
|
|
|
+
|
|
|
|
|
+import 'dart:convert';
|
|
|
|
|
+import 'dart:typed_data';
|
|
|
|
|
+import 'package:convert/convert.dart' show hex;
|
|
|
|
|
+
|
|
|
|
|
+
|
|
|
|
|
+
|
|
|
|
|
+/// Uint8List <--> Base64
|
|
|
|
|
+String bytesToBase64(Uint8List data, {bool urlSafe = false}) {
|
|
|
|
|
+ try {
|
|
|
|
|
+ if (data.isEmpty) {
|
|
|
|
|
+ throw ArgumentError('Input data cannot be empty');
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ final base64Str = base64Encode(data);
|
|
|
|
|
+
|
|
|
|
|
+ if (urlSafe) {
|
|
|
|
|
+ return base64Str
|
|
|
|
|
+ .replaceAll('+', '-')
|
|
|
|
|
+ .replaceAll('/', '_')
|
|
|
|
|
+ .replaceAll('=', '');
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ return base64Str;
|
|
|
|
|
+ } on ArgumentError catch (e) {
|
|
|
|
|
+ rethrow;
|
|
|
|
|
+ } catch (e) {
|
|
|
|
|
+ throw Exception('Failed to encode Uint8List to Base64: ${e.toString()}');
|
|
|
|
|
+ }
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+Uint8List base64ToBytes(String base64Str, {bool? urlSafe}) {
|
|
|
|
|
+ try {
|
|
|
|
|
+ if (base64Str.isEmpty) {
|
|
|
|
|
+ throw ArgumentError('Base64 string cannot be empty');
|
|
|
|
|
+ }
|
|
|
|
|
+ // 如果未指定 urlSafe,自动检测格式
|
|
|
|
|
+ final isUrlSafe = urlSafe ??
|
|
|
|
|
+ (base64Str.contains('-') || base64Str.contains('_'));
|
|
|
|
|
+
|
|
|
|
|
+ String normalizedStr = base64Str;
|
|
|
|
|
+
|
|
|
|
|
+ if (isUrlSafe) {
|
|
|
|
|
+ // 添加必要的填充字符
|
|
|
|
|
+ final padding = '=' * ((4 - base64Str.length % 4) % 4);
|
|
|
|
|
+ normalizedStr = base64Str
|
|
|
|
|
+ .replaceAll('-', '+')
|
|
|
|
|
+ .replaceAll('_', '/') + padding;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // 验证 Base64 字符串格式
|
|
|
|
|
+ if (!RegExp(r'^[a-zA-Z0-9+/]*={0,2}$').hasMatch(normalizedStr)) {
|
|
|
|
|
+ throw FormatException('Invalid Base64 string format');
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ return base64Decode(normalizedStr);
|
|
|
|
|
+ } on ArgumentError catch (e) {
|
|
|
|
|
+ rethrow;
|
|
|
|
|
+ } on FormatException catch (e) {
|
|
|
|
|
+ rethrow;
|
|
|
|
|
+ } catch (e) {
|
|
|
|
|
+ throw Exception('Failed to decode Base64 to Uint8List: ${e.toString()}');
|
|
|
|
|
+ }
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+
|
|
|
|
|
+
|
|
|
|
|
+/// 将十六进制字符串直接转换为 Base64
|
|
|
|
|
+///
|
|
|
|
|
+/// [hexStr] 十六进制字符串
|
|
|
|
|
+/// [urlSafe] 是否生成URL安全的Base64
|
|
|
|
|
+///
|
|
|
|
|
+/// 返回 Base64 字符串或抛出异常
|
|
|
|
|
+String hexToBase64(String hexStr, {bool urlSafe = false}) {
|
|
|
|
|
+ try {
|
|
|
|
|
+ if (hexStr.isEmpty) {
|
|
|
|
|
+ throw ArgumentError('Hex string cannot be empty');
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ final normalizedHex = hexStr.startsWith('0x')
|
|
|
|
|
+ ? hexStr.substring(2)
|
|
|
|
|
+ : hexStr;
|
|
|
|
|
+
|
|
|
|
|
+ if (!RegExp(r'^[0-9a-fA-F]+$').hasMatch(normalizedHex)) {
|
|
|
|
|
+ throw FormatException('Invalid hex string format');
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ final bytes = hex.decode(normalizedHex.padLeft(
|
|
|
|
|
+ normalizedHex.length + normalizedHex.length % 2,
|
|
|
|
|
+ '0'
|
|
|
|
|
+ ));
|
|
|
|
|
+
|
|
|
|
|
+ final base64 = base64Encode(bytes);
|
|
|
|
|
+ return urlSafe
|
|
|
|
|
+ ? base64.replaceAll('+', '-').replaceAll('/', '_').replaceAll('=', '')
|
|
|
|
|
+ : base64;
|
|
|
|
|
+ } on ArgumentError catch (e) {
|
|
|
|
|
+ rethrow;
|
|
|
|
|
+ } on FormatException catch (e) {
|
|
|
|
|
+ rethrow;
|
|
|
|
|
+ } catch (e) {
|
|
|
|
|
+ throw Exception('Failed to convert hex to Base64: ${e.toString()}');
|
|
|
|
|
+ }
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+
|
|
|
|
|
+/// 将 Base64 字符串转换为十六进制字符串
|
|
|
|
|
+///
|
|
|
|
|
+/// [base64Str] 输入的 Base64 字符串
|
|
|
|
|
+/// [urlSafe] 输入是否是 URL 安全的 Base64 编码
|
|
|
|
|
+/// [withPrefix] 是否添加 "0x" 前缀
|
|
|
|
|
+///
|
|
|
|
|
+/// 返回十六进制字符串或抛出异常
|
|
|
|
|
+String base64ToHex(String base64Str, {
|
|
|
|
|
+ bool urlSafe = false,
|
|
|
|
|
+ bool withPrefix = false,
|
|
|
|
|
+}) {
|
|
|
|
|
+ try {
|
|
|
|
|
+ if (base64Str.isEmpty) {
|
|
|
|
|
+ throw ArgumentError('Base64 string cannot be empty');
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ String normalizedStr = base64Str;
|
|
|
|
|
+
|
|
|
|
|
+ // 处理 URL 安全的 Base64
|
|
|
|
|
+ if (urlSafe) {
|
|
|
|
|
+ // 添加必要的填充字符
|
|
|
|
|
+ final padding = '=' * ((4 - base64Str.length % 4) % 4);
|
|
|
|
|
+ normalizedStr = base64Str
|
|
|
|
|
+ .replaceAll('-', '+')
|
|
|
|
|
+ .replaceAll('_', '/') + padding;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // 验证 Base64 字符串格式
|
|
|
|
|
+ if (!RegExp(r'^[a-zA-Z0-9+/]*={0,2}$').hasMatch(normalizedStr)) {
|
|
|
|
|
+ throw FormatException('Invalid Base64 string format');
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // 解码为字节数组
|
|
|
|
|
+ final bytes = base64Decode(normalizedStr);
|
|
|
|
|
+
|
|
|
|
|
+ // 转换为十六进制
|
|
|
|
|
+ final hexString = bytes.map((byte) {
|
|
|
|
|
+ return byte.toRadixString(16).padLeft(2, '0');
|
|
|
|
|
+ }).join('');
|
|
|
|
|
+
|
|
|
|
|
+ return withPrefix ? '0x$hexString' : hexString;
|
|
|
|
|
+ } on ArgumentError catch (e) {
|
|
|
|
|
+ rethrow;
|
|
|
|
|
+ } on FormatException catch (e) {
|
|
|
|
|
+ rethrow;
|
|
|
|
|
+ } catch (e) {
|
|
|
|
|
+ throw Exception('Failed to convert Base64 to hex: ${e.toString()}');
|
|
|
|
|
+ }
|
|
|
|
|
+}
|
|
|
|
|
+
|