编程实现QQ表情文件CFC格式
背景:最近闲来无事,也应论坛某会员要求,想做个qq表情下载的站点。本来事情是很简单的,写个小小的crud也就可以了,但嘻哈呵嘿既然是个.net程序员,当然要使用.net来实现了。今天我们就用.net来实现cfc ( custom face cab? ) 的表情格式的打包功能。 要做到这个功能,我们必须先了解这个格式,首先google一下。我们找到了这一篇来自清华大学的文章:fc文件格式详解 从这篇文章里我们得知了cfc的文件格式大概如下: 一个块有15个字段,如下 md5的字符串形式长度,4个字节 我们再建一个类,命名为:facehelper //得到一个唯一的md5字符串 //取得总的帧数 fb.md5 = md5; return fb; helper#region helper datetime.now.ticks)); //从文件名获得md5 return str.toupper(); //从一个目录生成一个cfc文件集合 bytes.addrange(faceblock.fromimage(file).tobytes()); string fname = path.combine(directory, path.getdirectoryname(directory) + ".cfc"); //判断是否为图像文件,方法比较简陋。 return validext.contains(path.getextension(file).tolower()); facehelper.buildcfcfilefromdirectory(server.mappath("~/img/")); 本文原发:http://www.cnblogs.com/skyover/archive/2006/10/03/520581.html
快捷键长度,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<byte> bytes = new list<byte>();
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<byte> bytes = new list<byte>();
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数组。
代码如下:
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();
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.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;
}
//随机方法[www.iocblog.net 来源]
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)
}
//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;//返回生成的随机数
}
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));
}
}
#endregion
public static void
buildcfcfilefromdirectory(string directory)
{
list<byte> bytes = new list<byte>();
foreach (string file in directory.getfiles(directory))
{
if (!isimagefile(file))
continue;
}
filestream fs = file.create(fname);
fs.write(bytes.toarray(), 0, bytes.count);
fs.close();
}
private static bool isimagefile(string file)
{
list<string> validext = new list<string>(new string[]{
".bmp",
".jpg",
".jpeg",
".gif",
".png",
});
}
}
好,有了上面的方法,我们就可以调用了。
调用方法实在是有些简单。[www.iocblog.net 来源]
这样就ok了,现在去你的网站根目录下看看,有没有一个img.cfc的文件呢?再双击一下,是不是将img目录下的文件全部导入到qq表情里了呢? enjoy coding!