Burnett

淡定,执着......
posts - 29, comments - 109, trackbacks - 22, articles - 10
  博客园 :: 首页 :: 新随笔 :: 联系 :: 订阅 订阅 :: 管理

公告

谁动了我的Infopath附件?-Infopath上传文件产生“文件损坏”

Posted on 2007-10-17 17:23 Burnett 阅读(...) 评论(...) 编辑 收藏
  最近接到了一个需求,需要将保存在Infopath表单中的数据提取出来。可是这么做出现了一个问题,就是经过IP上传的文件,似乎都被IP的附件控件增加了一些数据,这会导致一种颇为致命的错误,这么来做会破坏原文件的二进制布局。如果容错性不好的程序,会直接报错。比如我把word文件提出来序列化到硬盘上打开,2003会直接提示错误,2007倒是在提示错误以后,可以自动修复。不过我们不需要这种功能。
 Here we go!
 首先应该找出,调皮的Infopath到底将哪些数据藏在了文件的什么地方。动手写了一个程序,将未处理过的文件和被处理过的文件逐字节匹配,遇到不匹配的数据以后会尝试查找匹配的数据。
            FileStream fs1 = new FileStream(@"c:\source", FileMode.Open);
            FileStream fs2 
= new FileStream(@"c:\dest", FileMode.Open);
            BinaryReader brSrc 
= new BinaryReader(fs1);
            BinaryReader brDst 
= new BinaryReader(fs2);
            
byte[] src=brSrc.ReadBytes(int.Parse(brSrc.BaseStream.Length.ToString()));
            
byte[] dst = brDst.ReadBytes(int.Parse(brDst.BaseStream.Length.ToString()));
            
int destPtr = 0;
            
for (int srcPtr = 0; srcPtr < src.Length; srcPtr++, destPtr++)
            {

                
if (dst[destPtr] != src[srcPtr])
                {
                    Console.WriteLine(
string.Format("目标文件的第{0}位置出现与源文件{1}不匹配的情况,不匹配的字符{2}",srcPtr,destPtr,dst[destPtr]));
                    
                    
for (; destPtr < dst.Length; destPtr++)
                    {
                        
if (dst[destPtr] == src[srcPtr])
                        {
                            Console.WriteLine(String.Format(
"在第{0}个位置上,找到对应byte", destPtr)); Console.Read();
                            
break;
                        }
                    }
                }
                
else
                    Console.WriteLine(
"source的第{0}位与dest的第{1}位匹配",srcPtr,destPtr);                
            }
            Console.Read();

经过测试发现,新的文件比老文件增大了58个字节,看来我的猜测是对的,Infopath的确在文件中动了手脚!
再看一下结果:


还好,数据只被增加到了Infopath文件的头部。剩下的就是要分析头的格式,因为一般头都是可变长度的,所以分析格式可以动态的取出实际的infopath文件。

取出这58个字节,经过Unicode解码,发现了我上传文件的文件名。联想到infopath的xml文件中,并没有存有文件名的节点,但是仍然可以在infopath中将数据显示出来的情况,问题就很好解释了。

参考Infopath的官方博客的文章,http://blogs.msdn.com/infopath/archive/2004/03/18/92221.aspx
发现了这么一段话:
·         BYTE[4]: Signature (based on the signature for PNG): 

(decimal)                       
199       73         70         65
(hexadecimal)                C7        
49         46         41 
(ASCII C notation)          \
307      I           F          A  


The first byte is chosen as a non-ASCII value to reduce the probability that a text file may be misrecognized as a file attachment.  The rest identifies the file as an InfoPath File Attachment.  


·         DWORD: Size of the header 

·         DWORD: IP Version 

·         DWORD: dwReserved 

·         DWORD: File size 

·         DWORD: Size of file name buffer 

·         File name buffer: variable size 

注意到这个头文件的格式分为六个部分,除了BYTE[4]中的四个字节,其余五部分都是DWORD类型。注意,DWORD是双字,一字是两个字节,也就是说一个DWORD的大小是4个字节。问题到这里就很清楚了,前4*(4+1)个字节是固定不变的,其中,在第20到24字节保存的正是文件名的大小。



OK,现在我们要做的就是找出动态的文件头,并把他们从我们的文件中剔除出去!文件的偏移量应该是 24+文件名长度。

            int namebufferlen = dst[20* 2;
            
byte[] namebuf=new byte[namebufferlen];
            
int headLength = 24 + namebufferlen;
            
string name = Encoding.Unicode.GetString(dst, 0, headLength);
            
byte[] realContent = new byte[dst.Length - headLength];

realContent就是我们实际的文件的内容!

Enjoy Infopath!