背景:最近閒來無事,也應論壇某會員要求,想做QQ表情下載的網站。本來事情是很簡單的,寫個小小的CRUD也就可以了,但嘻哈呵嘿既然是個.Net程式設計師,當然要使用.Net來實現了。今天我們就用.Net來實現CFC ( custom face cab? ) 的表情格式的打包功能。
要做到這個功能,我們必須先了解這個格式,先Google一下。我們找到了這篇來自清華大學的文章:FC文件格式詳解
從這篇文章裡我們得知了CFC的文件格式大概如下:
一個區塊有15個字段,如下
md5的字串形式長度,4個位元組快捷鍵長度,4位元組表情名稱長度,4位元組表情檔案名稱長度,4位元組表情檔案長度,4位元組微縮圖檔案名稱長度,4位元組微縮檔案長度,4位元組表情檔案幀數,4位元組圖片md5的字串形式快捷鍵表情名稱表情檔案名稱微縮圖檔案名稱表情檔案內容微縮圖內容知道了格式就好辦了,我們按步就班定義一個結構(struct)
1 Struct#region Struct
2 public struct FaceBlock
3 {
4 public uint MD5Length; //32
5 public uint uintcutLength; //4
6 public uint FaceNameLength; //4
7 public uint FaceFileNameLength; //36 md5 + extension
8 public uint FileLength;
9 public uint ThumbnailFileNameLength; //41 md5 + fixed.bmp
10 public uint ThumbnailFileLength;
11 public uint FrameLength;
12 public string MD5;
13 public string uintcuts;
14 public string FaceName;
15 public string FaceFileName;
16 public string ThumbnailFileName;
17 public byte[] FaceData;
18 public byte[] ThumbnailData;
19
20 public static FaceBlock FromImage(string file)
21 {
22 return FaceHelper.GetFaceBlockFromImage(file);
23 }
24
25 byte[] GetBytes(uint value)
26 {
27 byte[] bt = BitConverter.GetBytes(value);
28 List
29 bytes.AddRange(bt);
30 if (bytes.Count < 4)
31 {
32 int l = 4 - bytes.Count;
33 for (int i = 0; i < l; i++)
34 bytes.Add((byte)0);
35 }
36 return bytes.ToArray();
37 }
38
39 public byte[] ToBytes()
40 {
41 List
42 Encoding e = Encoding.ASCII;
43 bytes.AddRange(GetBytes(MD5Length));
44 bytes.AddRange(GetBytes(uintcutLength));
45 bytes.AddRange(GetBytes(FaceNameLength));
46 bytes.AddRange(GetBytes(FaceFileNameLength));
47 bytes.AddRange(GetBytes(FileLength));
48 bytes.AddRange(GetBytes(ThumbnailFileNameLength));
49 bytes.AddRange(GetBytes(ThumbnailFileLength));
50 bytes.AddRange(GetBytes(FrameLength));
51
52 bytes.AddRange(e.GetBytes(MD5));
53 bytes.AddRange(e.GetBytes(uintcuts));
54 bytes.AddRange(e.GetBytes(FaceName));
55 bytes.AddRange(e.GetBytes(FaceFileName));
56 bytes.AddRange(e.GetBytes(ThumbnailFileName));
57
58 bytes.AddRange(FaceData);
59 bytes.AddRange(ThumbnailData);
60
61 return bytes.ToArray();
62 }
63 }
64 #endregion其中含有兩個方法,一個是從檔案得到一個此結構的靜態方法,另一個是將此結構轉換為byte陣列。
我們再建一個類,命名為:FaceHelper
程式碼如下:
public class FaceHelper
{
internal static FaceBlock GetFaceBlockFromImage(string file)
{
FaceBlock fb = new FaceBlock();
//開啟檔案流
FileStream fs = new FileStream(file, FileMode.Open, FileAccess.Read);
//取得影像
Image img = Image.FromStream(fs);
//獲得一個20*20的縮圖
Image thumbnail = img.GetThumbnailImage(20, 20, null, IntPtr.Zero);
MemoryStream ms = new MemoryStream();
//將縮圖圖轉換數byte數組
thumbnail.Save(ms, System.Drawing.Imaging.ImageFormat.Bmp);
byte[] thumbnailData = ms.ToArray();
ms.Close();
ms.Dispose();
thumbnail.Dispose();
//得到一個唯一的MD5字串
string md5 = GetMD5(fs);
//檔名,格式為:md5 + 副檔名
string fileName = string.Format("{0}{1}", md5, Path.GetExtension(file));
//縮圖檔名,格式為:md5 + fixed.bmp
string thumbnailName = string.Format("{0}fixed.bmp", md5);
//隨機設定一個快捷鍵
string uintcuts = "qq.5inet.net_" + RandomNum(6);
fs.Close();
fs.Dispose();
//取得總的幀數
System.Drawing.Imaging.FrameDimension fd = System.Drawing.Imaging.FrameDimension.Resolution;
int frameCount = img.FrameDimensionsList.Length;
img.Dispose();
fb.MD5 = md5;
fb.MD5Length = (uint)md5.Length;
fb.uintcuts = uintcuts;
fb.uintcutLength = (uint)uintcuts.Length;
fb.FaceName = uintcuts;
fb.FaceNameLength = (uint)uintcuts.Length;
fb.FaceFileName = fileName;
fb.FaceFileNameLength = (uint)fileName.Length;
fb.ThumbnailFileName = thumbnailName;
fb.ThumbnailFileNameLength = (uint)thumbnailName.Length;
fb.FaceData = File.ReadAllBytes(file);
fb.FileLength = (uint)fb.FaceData.Length;
fb.ThumbnailData = thumbnailData;
fb.ThumbnailFileLength = (uint)thumbnailData.Length;
fb.FrameLength = (uint)frameCount;
return fb;
}
Helper#region Helper
//隨機法
internal static string RandomNum(int n) //
{
string strchar = "0,1,2,3,4,5,6,7,8,9";
string[] VcArray = strchar.Split(',');
string VNum = "";//由於字串很短,F77pclw,c絡G|?,業,e'b就不用StringBuilder了
int temp = -1; //記錄上次隨機數值,盡量避免產生幾個相同的隨機數
//採用簡單的演算法來確保產生隨機數的不同
Random rand = new Random();
for (int i = 1; i < n + 1; i++)
{
if (temp != -1)
{
rand = new Random(i * temp * unchecked((int)
DateTime.Now.Ticks));
}
//int t = rand.Next(35) ;
int t = rand.Next(10);
if (temp != -1 && temp == t)
{
return RandomNum(n);
}
temp = t;
VNum += VcArray[t];
}
return VNum;//傳回產生的隨機數
}
//從檔案名稱取得MD5
internal static string GetMD5(FileStream fs)
{
MD5CryptoServiceProvider md5 = new MD5CryptoServiceProvider();
byte[] md5byte = md5.ComputeHash(fs);
string str = string.Empty;
int i, j;
foreach (byte b in md5byte)
{
i = Convert.ToInt32(b);
j = i >> 4;
str += (Convert.ToString(j, 16));
j = ((i << 4) & 0x00ff) >> 4;
str += (Convert.ToString(j, 16));
}
return str.ToUpper();
}
#endregion
//從一個目錄產生一個CFC檔案集合
public static void
BuildCFCFileFromDirectory(string directory)
{
List
foreach (string file in Directory.GetFiles(directory))
{
if (!IsImageFile(file))
continue;
bytes.AddRange(FaceBlock.FromImage(file).ToBytes());
}
string fName = Path.Combine(directory, Path.GetDirectoryName(directory) + ".cfc");
FileStream fs = File.Create(fName);
fs.Write(bytes.ToArray(), 0, bytes.Count);
fs.Close();
}
//判斷是否為圖片文件,方法比較簡陋。
private static bool IsImageFile(string file)
{
List
".bmp",
".jpg",
".jpeg",
".gif",
".png",
});
return validExt.Contains(Path.GetExtension(file).ToLower());
}
}
好,有了上面的方法,我們就可以呼叫了。
呼叫方法實在有些簡單。
FaceHelper.BuildCFCFileFromDirectory(Server.MapPath("~/img/"));
這樣就OK了,現在就去你的網站根目錄下看看,有沒有一個img.cfc的檔案呢?再雙擊一下,是不是將img目錄下的檔案全部導入QQ表情裡了呢? enjoy coding!
本文原發:無垠IT教學網
http://www.cnblogs.com/skyover/archive/2006/10/03/520581.html