热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

编程实现QQ表情文件CFC格式

编程实现QQ表情文件CFC格式背景:最近闲来无事,也应论坛某会员要求,想做个QQ表情下载的站点。本来事情是很简单的,写个小小
编程实现QQ表情文件CFC格式

背景:最近闲来无事,也应论坛某会员要求,想做个QQ表情下载的站点。本来事情是很简单的,写个小小的CRUD也就可以了,但嘻哈呵嘿既然是个.Net程序员,当然要使用.Net来实现了。今天我们就用.Net来实现CFC ( custom face cab? ) 的表情格式的打包功能。

要做到这个功能,我们必须先了解这个格式,首先Google一下。我们找到了这一篇来自清华大学的文章:FC文件格式详解

从这篇文章里我们得知了CFC的文件格式大概如下:

一个块有15个字段,如下

  1. md5的字符串形式长度,4个字节
  2. 快捷键长度,4字节
  3. 表情名称长度,4字节
  4. 表情文件名长度,4字节
  5. 表情文件长度,4字节
  6. 微缩图文件名长度,4字节
  7. 微缩文件长度,4字节
  8. 表情文件帧数,4字节
  9. 图片md5的字符串形式
  10. 快捷键
  11. 表情名称
  12. 表情文件名
  13. 微缩图文件名
  14. 表情文件内容
  15. 微缩图内容
知道了格式就好办了,我们按步就班定义一个结构(struct)
 1ContractedBlock.gifExpandedBlockStart.gif    Struct#region Struct
 2InBlock.gif    public struct FaceBlock
 3ExpandedSubBlockStart.gifContractedSubBlock.gif    dot.gif{
 4InBlock.gif        public uint MD5Length; //32
 5InBlock.gif        public uint uintcutLength; //4
 6InBlock.gif        public uint FaceNameLength; //4
 7InBlock.gif        public uint FaceFileNameLength; //36 md5 + extension
 8InBlock.gif        public uint FileLength;
 9InBlock.gif        public uint ThumbnailFileNameLength; //41 md5 + fixed.bmp
10InBlock.gif        public uint ThumbnailFileLength;
11InBlock.gif        public uint FrameLength;
12InBlock.gif        public string MD5;
13InBlock.gif        public string uintcuts;
14InBlock.gif        public string FaceName;
15InBlock.gif        public string FaceFileName;
16InBlock.gif        public string ThumbnailFileName;
17InBlock.gif        public byte[] FaceData;
18InBlock.gif        public byte[] ThumbnailData;
19InBlock.gif
20InBlock.gif        public static FaceBlock FromImage(string file)
21ExpandedSubBlockStart.gifContractedSubBlock.gif        dot.gif{
22InBlock.gif            return FaceHelper.GetFaceBlockFromImage(file);
23ExpandedSubBlockEnd.gif        }

24InBlock.gif
25InBlock.gif        byte[] GetBytes(uint value)
26ExpandedSubBlockStart.gifContractedSubBlock.gif        dot.gif{
27InBlock.gif            byte[] bt = BitConverter.GetBytes(value);
28InBlock.gif            List<byte> bytes &#61; new List<byte>();
29InBlock.gif            bytes.AddRange(bt);
30InBlock.gif            if (bytes.Count < 4)
31ExpandedSubBlockStart.gifContractedSubBlock.gif            dot.gif{
32InBlock.gif                int l &#61; 4 - bytes.Count;
33InBlock.gif                for (int i &#61; 0; i < l; i&#43;&#43;)
34InBlock.gif                    bytes.Add((byte)0);
35ExpandedSubBlockEnd.gif            }

36InBlock.gif            return bytes.ToArray();
37ExpandedSubBlockEnd.gif        }

38InBlock.gif
39InBlock.gif        public byte[] ToBytes()
40ExpandedSubBlockStart.gifContractedSubBlock.gif        dot.gif{
41InBlock.gif            List<byte> bytes &#61; new List<byte>();
42InBlock.gif            Encoding e &#61; Encoding.ASCII;
43InBlock.gif            bytes.AddRange(GetBytes(MD5Length));
44InBlock.gif            bytes.AddRange(GetBytes(uintcutLength));
45InBlock.gif            bytes.AddRange(GetBytes(FaceNameLength));
46InBlock.gif            bytes.AddRange(GetBytes(FaceFileNameLength));
47InBlock.gif            bytes.AddRange(GetBytes(FileLength));
48InBlock.gif            bytes.AddRange(GetBytes(ThumbnailFileNameLength));
49InBlock.gif            bytes.AddRange(GetBytes(ThumbnailFileLength));
50InBlock.gif            bytes.AddRange(GetBytes(FrameLength));
51InBlock.gif
52InBlock.gif            bytes.AddRange(e.GetBytes(MD5));
53InBlock.gif            bytes.AddRange(e.GetBytes(uintcuts));
54InBlock.gif            bytes.AddRange(e.GetBytes(FaceName));
55InBlock.gif            bytes.AddRange(e.GetBytes(FaceFileName));
56InBlock.gif            bytes.AddRange(e.GetBytes(ThumbnailFileName));
57InBlock.gif
58InBlock.gif            bytes.AddRange(FaceData);
59InBlock.gif            bytes.AddRange(ThumbnailData);
60InBlock.gif
61InBlock.gif            return bytes.ToArray();
62ExpandedSubBlockEnd.gif        }

63ExpandedSubBlockEnd.gif    }

64ExpandedBlockEnd.gif    #endregion
其中含有两方法&#xff0c;一个是从文件得到一个此结构的静态方法&#xff0c;另一个是将此结构转化为byte数组。

我们再建一个类&#xff0c;命名为&#xff1a;FaceHelper
代码如下&#xff1a;
None.gif    public class FaceHelper
ExpandedBlockStart.gifContractedBlock.gif    
dot.gif{
InBlock.gif        
internal static FaceBlock GetFaceBlockFromImage(string file)
ExpandedSubBlockStart.gifContractedSubBlock.gif        
dot.gif{
InBlock.gif            FaceBlock fb 
&#61; new FaceBlock();
InBlock.gif            
//打开文件流   
InBlock.gif
            FileStream fs &#61; new FileStream(file, FileMode.Open, FileAccess.Read);
InBlock.gif            
//获取图像
InBlock.gif
            Image img &#61; Image.FromStream(fs);
InBlock.gif            
//获得一个20*20的缩略图
InBlock.gif
            Image thumbnail &#61; img.GetThumbnailImage(2020null, IntPtr.Zero);
InBlock.gif            MemoryStream ms 
&#61; new MemoryStream();
InBlock.gif            
//将缩图图转化数byte数组
InBlock.gif
            thumbnail.Save(ms, System.Drawing.Imaging.ImageFormat.Bmp);
InBlock.gif            
byte[] thumbnailData &#61; ms.ToArray();
InBlock.gif            ms.Close();
InBlock.gif            ms.Dispose();
InBlock.gif            thumbnail.Dispose();
InBlock.gif
InBlock.gif            
//得到一个唯一的MD5字符串
InBlock.gif
            string md5 &#61; GetMD5(fs);
InBlock.gif            
//文件名&#xff0c;格式为:md5 &#43; 扩展名
InBlock.gif
            string fileName &#61; string.Format("{0}{1}", md5, Path.GetExtension(file));
InBlock.gif            
//缩略图文件名&#xff0c;格式为&#xff1a;md5 &#43; fixed.bmp
InBlock.gif
            string thumbnailName &#61; string.Format("{0}fixed.bmp", md5);
InBlock.gif            
//随机设置一个快捷键
InBlock.gif
            string uintcuts &#61; "qq.5inet.net_" &#43; RandomNum(6);
InBlock.gif            fs.Close();
InBlock.gif            fs.Dispose();
InBlock.gif
InBlock.gif            
//取得总的帧数
InBlock.gif
System.Drawing.Imaging.FrameDimension fd &#61; System.Drawing.Imaging.FrameDimension.Resolution;
InBlock.gif            
int frameCount &#61; img.FrameDimensionsList.Length;
InBlock.gif            img.Dispose();
InBlock.gif
InBlock.gif            fb.MD5 
&#61; md5;
InBlock.gif            fb.MD5Length 
&#61; (uint)md5.Length;
InBlock.gif            fb.uintcuts 
&#61; uintcuts;
InBlock.gif            fb.uintcutLength 
&#61; (uint)uintcuts.Length;
InBlock.gif            fb.FaceName 
&#61; uintcuts;
InBlock.gif            fb.FaceNameLength 
&#61; (uint)uintcuts.Length;
InBlock.gif            fb.FaceFileName 
&#61; fileName;
InBlock.gif            fb.FaceFileNameLength 
&#61; (uint)fileName.Length;
InBlock.gif            fb.ThumbnailFileName 
&#61; thumbnailName;
InBlock.gif            fb.ThumbnailFileNameLength 
&#61; (uint)thumbnailName.Length;
InBlock.gif            fb.FaceData 
&#61; File.ReadAllBytes(file);
InBlock.gif            fb.FileLength 
&#61; (uint)fb.FaceData.Length;
InBlock.gif            fb.ThumbnailData 
&#61; thumbnailData;
InBlock.gif            fb.ThumbnailFileLength 
&#61; (uint)thumbnailData.Length;
InBlock.gif            fb.FrameLength 
&#61; (uint)frameCount;
InBlock.gif
InBlock.gif            
return fb;
ExpandedSubBlockEnd.gif        }

InBlock.gif
ContractedSubBlock.gifExpandedSubBlockStart.gif        
Helper#region Helper
InBlock.gif        
//随机方法
InBlock.gif
        internal static string RandomNum(int n) //
ExpandedSubBlockStart.gifContractedSubBlock.gif
        dot.gif{
InBlock.gif            
string strchar &#61; "0,1,2,3,4,5,6,7,8,9";
InBlock.gif            
string[] VcArray &#61; strchar.Split(&#39;,&#39;);
InBlock.gif            
string VNum &#61; "";//由于字符串很短&#xff0c;F77pclw,c络G|?,业,e&#39;b就不用StringBuilder了
InBlock.gif
            int temp &#61; -1;    //记录上次随机数值&#xff0c;尽量避免产生几个一样的随机数
InBlock.gif            
//采用一个简单的算法以保证生成随机数的不同
InBlock.gif
            Random rand &#61; new Random();
InBlock.gif            
for (int i &#61; 1; i < n &#43; 1; i&#43;&#43;)
ExpandedSubBlockStart.gifContractedSubBlock.gif            
dot.gif{
InBlock.gif                
if (temp !&#61; -1)
ExpandedSubBlockStart.gifContractedSubBlock.gif                
dot.gif{
InBlock.gif                    rand 
&#61; new Random(i * temp * unchecked((int)
InBlock.gif
InBlock.gif                 DateTime.Now.Ticks));
ExpandedSubBlockEnd.gif                }

InBlock.gif                
//int t &#61;  rand.Next(35) ;
InBlock.gif
                int t &#61; rand.Next(10);
InBlock.gif                
if (temp !&#61; -1 && temp &#61;&#61; t)
ExpandedSubBlockStart.gifContractedSubBlock.gif                
dot.gif{
InBlock.gif                    
return RandomNum(n);
ExpandedSubBlockEnd.gif                }

InBlock.gif                temp 
&#61; t;
InBlock.gif                VNum 
&#43;&#61; VcArray[t];
ExpandedSubBlockEnd.gif            }

InBlock.gif            
return VNum;//返回生成的随机数
ExpandedSubBlockEnd.gif
        }

InBlock.gif
InBlock.gif        
//从文件名获得MD5
InBlock.gif
        internal static string GetMD5(FileStream fs)
ExpandedSubBlockStart.gifContractedSubBlock.gif        
dot.gif{
InBlock.gif            MD5CryptoServiceProvider md5 
&#61; new MD5CryptoServiceProvider();
InBlock.gif            
byte[] md5byte &#61; md5.ComputeHash(fs);
InBlock.gif            
string str &#61; string.Empty;
InBlock.gif            
int i, j;
InBlock.gif            
foreach (byte b in md5byte)
ExpandedSubBlockStart.gifContractedSubBlock.gif            
dot.gif{
InBlock.gif                i 
&#61; Convert.ToInt32(b);
InBlock.gif                j 
&#61; i >> 4;
InBlock.gif                str 
&#43;&#61; (Convert.ToString(j, 16));
InBlock.gif                j 
&#61; ((i << 4& 0x00ff>> 4;
InBlock.gif                str 
&#43;&#61; (Convert.ToString(j, 16));
ExpandedSubBlockEnd.gif            }

InBlock.gif
InBlock.gif            
return str.ToUpper();
ExpandedSubBlockEnd.gif        }

ExpandedSubBlockEnd.gif        
#endregion

InBlock.gif
InBlock.gif        
//从一个目录生成一个CFC文件集合
InBlock.gif
        public static void 
InBlock.gifBuildCFCFileFromDirectory(
string directory)
ExpandedSubBlockStart.gifContractedSubBlock.gif        
dot.gif{
InBlock.gif            List
<byte> bytes &#61; new List<byte>();
InBlock.gif            
foreach (string file in Directory.GetFiles(directory))
ExpandedSubBlockStart.gifContractedSubBlock.gif            
dot.gif{
InBlock.gif                
if (!IsImageFile(file))
InBlock.gif                    
continue;
InBlock.gif
InBlock.gif                bytes.AddRange(FaceBlock.FromImage(file).ToBytes());
ExpandedSubBlockEnd.gif            }

InBlock.gif
InBlock.gif            
string fName &#61; Path.Combine(directory, Path.GetDirectoryName(directory) &#43; ".cfc");
InBlock.gif            FileStream fs 
&#61; File.Create(fName);
InBlock.gif            fs.Write(bytes.ToArray(), 
0, bytes.Count);
InBlock.gif            fs.Close();
ExpandedSubBlockEnd.gif        }

InBlock.gif
InBlock.gif        
//判断是否为图像文件&#xff0c;方法比较简陋。
InBlock.gif
        private static bool IsImageFile(string file)
ExpandedSubBlockStart.gifContractedSubBlock.gif        
dot.gif{
ExpandedSubBlockStart.gifContractedSubBlock.gif            List
<string> validExt &#61; new List<string>(new string[]dot.gif{
InBlock.gif                
".bmp",
InBlock.gif                
".jpg",
InBlock.gif                
".jpeg",
InBlock.gif                
".gif",
InBlock.gif                
".png",
ExpandedSubBlockEnd.gif            }
);
InBlock.gif
InBlock.gif            
return validExt.Contains(Path.GetExtension(file).ToLower());
ExpandedSubBlockEnd.gif        }

ExpandedBlockEnd.gif    }

好&#xff0c;有了上面的方法&#xff0c;我们就可以调用了。
调用方法实在是有些简单。

None.gifFaceHelper.BuildCFCFileFromDirectory(Server.MapPath("~/img/"));

这样就OK了&#xff0c;现在去你的网站根目录下看看&#xff0c;有没有一个img.cfc的文件呢&#xff1f;再双击一下&#xff0c;是不是将img目录下的文件全部导入到QQ表情里了呢&#xff1f; enjoy coding!

本文原发&#xff1a;无垠IT教学网
如有不妥&#xff0c;请各位光临论坛指教。
posted on 2006-10-03 15:15 嘻哈呵嘿 阅读(...) 评论(...) 编辑 收藏

转:https://www.cnblogs.com/skyover/archive/2006/10/03/520581.html



推荐阅读
author-avatar
dsvsV
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有