C#中有对应的Base64处理函数:例如把byte[] 转成base64字符串Convert.ToBase64String
base64是用64个字符来表示任意二进制数据的方法,它对二进制数据进行处理,它把每6bit前缀补0转成8bit后,再查base64字符表得到转化后的结果。
base64编码所用的64个字符为:A-Za-z0-9+/
base64URL编码基于base64编码,把字符+和/分别变成-和_。
同理:还有base58, base58去掉了0(数字0)、O (大写字母O)、 I (大写的字母i) and l (小写的字母L) 、/、 +等。
在java8中有对应的处理函数:Base64.getUrlEncoder().encodeToString
C#中没有找到对应的处理函数,基于以上理解可以直接做字符替换:
string temp = Convert.ToBase64String(dataBytes);
string basedTimeStr = temp.Replace("+", "-").Replace("/", "_");
应用实例:unix时间戳的编码成6位字符串的算法:
java代码为:
public class Base64URLEncoder {
public static String encode(long ts) {
return Base64.getUrlEncoder().encodeToString(ByteBuffer.allocate(Long.BYTES).putLong((ts / 1000) << 28).array())
.substring(0, 6);
}
C#代码为:
/// <summary>
/// 时间编码
/// </summary>
/// <param name="dtNow">当前本地时间</param>
/// <returns></returns>
string GetDate(DateTime dtNow)
{
DateTime dtStart = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc);
TimeSpan span = dtNow.ToUniversalTime() - dtStart;
long currentTimeSeconds= (long)(span.TotalSeconds) << 28;
var dataBytes = BitConverter.GetBytes(currentTimeSeconds);
Array.Reverse(dataBytes);
string temp = Convert.ToBase64String(dataBytes);
string basedTimeStr = temp.Replace("+", "-").Replace("/", "_"); ;
string result = basedTimeStr.Substring(0, 6);//只返回前6位
return result;
}
/// <summary>
/// 时间解码
/// </summary>
/// <param name="code"></param>
/// <returns>返回本地时间</returns>
DateTime DeCodeDate(string code)
{
byte[] bytes = Convert.FromBase64String(code);
Array.Reverse(bytes);
ulong tt = BitConverter.ToUInt64(bytes, 0);
DateTime dtStart = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc);
//DateTime dtStart = TimeZone.CurrentTimeZone.ToLocalTime(new DateTime(1970, 1, 1, 0, 0, 0, 0));
long unixTime = (long)tt >> 28;
return dtStart.AddSeconds(unixTime).ToLocalTime();
}
对应的另外一种算法2,对64取模:
string s_base64url = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_";
string GetDate2(DateTime dtNow)
{
DateTime dtStart = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc);
TimeSpan span = dtNow.ToUniversalTime() - dtStart;
long sek =(long) Math.Floor(span.TotalSeconds);
StringBuilder stringBuilder = new StringBuilder();
while (sek > 0)
{
long n = sek % 64;
char baseStr = s_base64url[(int)n];
stringBuilder.Insert(0,baseStr);
sek = (long)Math.Floor((double)sek/64);
}
return stringBuilder.ToString().PadLeft(6,'A');
}
算法2的计算过程同base58的方式,之所以计算1和计算2结果相同,是因为本质都是64进制。
对算法1中左移28位(<<28)的理解:
因为base64是每6bit一个转码,如果需求是把时间戳转成6位字符,则时间戳从低位开始的6*6=36有效,剩下64-36=28位都是0,其他位都是1,则最大可表示的年月日为:4147/8/20 15:32:15
string longBinaryStr = "00000000 00000000 00000000 00001111 11111111 11111111 11111111 11111111".Replace(" ","");
long unixTime = Convert.ToInt64(longBinaryStr, 2);
DateTime dt = dtStart.AddSeconds(unixTime).ToLocalTime();
<<28
基于上面的理解:写一个解码函数:
private void DecodeBase64URLString(string code)
{
string s_base64url = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_";
StringBuilder sb = new StringBuilder();
for (int i = 0; i < 6; i++)
{
byte ch_index = (byte)s_base64url.IndexOf(code[i]);
string tempBinary = Convert.ToString(ch_index, 2).PadLeft(6,'0');
sb.Append(tempBinary);
}
for (int i = sb.Length; i < 64; i++)
{
sb.Append("0");
}
long unix = Convert.ToInt64(sb.ToString(), 2);
DateTime dtStart = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc);
long test = (long)unix >> 28;
DateTime dtR= dtStart.AddSeconds(test).ToLocalTime();
this.txtDate.Text = dtR.ToString("yyyy-MM-dd HH:mm:ss");
}