Infopath是微软Office系列的一个新的成员,其主要作用就是用于制作各种表单。对于一个表单而言,主要就是两类文件,表单模版(.XSN)以及相关的数据文件(.XML)。在此,我主要介绍一下数据文件。Infopath的数据文件,就是xml文件,那么他为什么能用Infopath打开,并用相应的模板查看数据呢,奥妙就在他们文件头上。在文件头上,指定了xml是一个Infopath的表单,同时,他也指明了打开这个表单,用于展示数据的模板。下面是一个Infopath数据文件的头部信息。从这个投中,我们可以了解到,这是一个Infopath 2007的表单,其模板是My Document下的UserProfile.xsn。

 

<?xml version="1.0" encoding="UTF-8"?> 
<?mso-infoPathSolution solutionVersion="1.0.0.1" productVersion="12.0.0" PIVersion="1.0.0.0" href="file:///D:\My%20Documents\UserProfile.xsn" 
    name="urn:schemas-microsoft-com:office:infopath:UserProfile:-myXSD-2008-12-28T18-07-15" 
?> 
    
<?mso-application progid="InfoPath.Document" versionProgid="InfoPath.Document.2"?> 
    
<?mso-infoPath-file-attachment-present?> 
    
<my:myFields xmlns:my="http://schemas.microsoft.com/office/infopath/2003/myXSD/2008-12-28T18:07:15" 
                xml:lang
="zh-cn"> 
    
<my:Name>Xinhai</my:Name> 
    
<my:Age xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">30</my:Age> 
    
<my:Gander>M</my:Gander> 
    
<my:Phone>000000</my:Phone> 
    
<my:Email>abc@abc.com</my:Email> 
    
<my:CV xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">x0lGQRQAAAABAAAAAAAAAAgAAAALAAAAWABpAG4AaABhAGkALgB0AHgAdAAAAGxpeGluaGFp</my:CV> 
</my:myFields>

 

那么,我们在写程序创建一个指定模板的Infopath数据文档时,可以通过简单的创建一个xml文本文档的方式在创建一个Infopath数据文档,关键的就是要在XML的头部加上Infopath的相关处理信息。对于带有附件的表单文件,<?mso-infoPath-file-attachment-present?> 是必不可少的,如果没有加这一段,即使你把附件加到表单中,用户也无法打开该附件。对于如何生成每个字段的内容,并填入数据,不是本文的重点,有机会,另文叙述。那么言归正传,回到本文的主题,怎么用程序的方式把一个文件加到Infopath数据文档中去。

首先,我们需要了解XML文档是如何存储一个文件的。不管是文本文档,还是二进制文件,XML都会将其编码成Base64编码,插入到XML节点当中。在RFC2045(http://www.ietf.org/rfc/rfc2045.txt)中Base64被定义为:Base64内容传送编码被设计用来把任意序列的8位字节描述为一种不易被人直接识别的形式。(The Base64 Content-Transfer-Encoding is designed to represent arbitrary sequences of octets in a form that need not be humanly readable.)字面上的意思就是把要传送的内容简单的加密,不能让人直接识别。Base64是一中纯ASCII编码,及高位为0的ASCII编码,因此,不会出现乱码或者与XML预定义字符冲突的现象。在C#语言中,可以很方便的通过Convert.ToBase64String(byte[])和Convert.FromBase64String(String)将一个二进制数据流与一个Base64的字符串进行互换。那么是不是只要简单的讲一个文件流转换成一个Base64字符串加到XML表单数据文件中,Infopath就能解析出文件来呢?答案是否定的。

其次,Infopath有其自己的格式来保存文件。我们再次查看上面的XML样本,仔细看看my:CV一节。显然,这是一个Base64的字符串,包含了文件的内容,但是奇怪的是,我们看不出文件名在哪。但实际上,我们用Infopath是可以看到附件的文件名的。那么,Infopath是怎么获取到文件名的呢?原来,Infopath在把文件序列化成Base64字串前,把文件名等信息也加入到其中了。Infopath建立了一个头部结构来保留文件相关信息,这个头包含如下内容:

    • BYTE[4].  文件签名.

(decimal)

199

73

70

65

(hexadecimal)

C7

49

46

41

(ASCII C notation)

\307

I

F

A

注意:这四位标示着一个文件的启示,没有实际的意义

  • DWORD. 头部结构的字节数,该大小包括本字段以及后面的四个字段。对于Infopath来说,这个字段的值一般都是20。
  • DWORD. 头部结构版本,对于Infopath 2003 SP1来说,这个值为1,经测试,Infopath 2007也可以将这个值设为1。
  • DWORD. 保留字段
  • DWORD. 文件大小
  • DWORD. 文件名长度
  • File name buffer. 可变大小

在这个头部结构之后,就是相应的文件数据了。

我们在了解了Infopath的附件格式之后,就能用程序去读取或者写入附件了。

读取附件的代码部分:

 

private const int SP1Header_Size = 20
private const int FIXED_HEADER = 16

private int fileSize; 
private int attachmentNameLength; 
private string attachmentName; 
private byte[] decodedAttachment; 

/// <summary> 
/// 解码基于Base64编码的Infopath附件字符串, 
///并将解码后的文件名和文件内容保存为内部私有变量 
/// </summary> 
/// <param name="theBase64EncodedString">Base64编码的字符串</param> 
public void InfoPathAttachmentDecode(string theBase64EncodedString) 

    
byte[] theData = Convert.FromBase64String(theBase64EncodedString); 
    
using (MemoryStream ms = new MemoryStream(theData)) 
    { 
        BinaryReader theReader 
= new BinaryReader(ms); 
        DecodeAttachment(theReader); 
    } 


private void DecodeAttachment(BinaryReader theReader) 

    
//获取头部结构数据 
    byte[] headerData = new byte[FIXED_HEADER]; 
    headerData 
= theReader.ReadBytes(headerData.Length); 

    fileSize 
= (int)theReader.ReadUInt32(); 
    attachmentNameLength 
= (int)theReader.ReadUInt32() * 2

//获取文件名流 
    byte[] fileNameBytes = theReader.ReadBytes(attachmentNameLength); 
    
//InfoPath 使用的是UTF8编码,将文件名转换为UTF8字符串 
    Encoding enc = Encoding.Unicode; 
    attachmentName 
= enc.GetString(fileNameBytes, 0, attachmentNameLength - 2); 
    
//获取附件内容,并将其保存为二进制数组 
    decodedAttachment = theReader.ReadBytes(fileSize); 
}

 

将附件转化为Infopath可识别Base64字串的代码段

 

private string base64EncodedFile = string.Empty; 
private string fullyQualifiedFileName; 

/// <summary> 
/// 将指定文件编码成Infopath可识别Base64字符串 
/// </summary> 
/// <param name="fullyQualifiedFileName">文件全路径</param> 
public InfoPathAttachmentEncoder(string fullyQualifiedFileName) 

    
if (fullyQualifiedFileName == string.Empty) 
        
throw new ArgumentException("Must specify file name""fullyQualifiedFileName"); 

    
if (!File.Exists(fullyQualifiedFileName)) 
        
throw new FileNotFoundException("File does not exist: " + fullyQualifiedFileName, fullyQualifiedFileName); 

    
this.fullyQualifiedFileName = fullyQualifiedFileName; 


/// <summary> 
/// 获取Based64编码字串. 
/// </summary> 
/// <returns>String</returns> 
public string ToBase64String() 

    
if (base64EncodedFile != string.Empty) 
        
return base64EncodedFile; 

    
// MemoryStream将用于保存文件附件数据流 
    MemoryStream ms = new MemoryStream(); 

    
// 获取文件信息. 
    using (BinaryReader br = new BinaryReader(File.Open(fullyQualifiedFileName, FileMode.Open, FileAccess.Read, FileShare.Read))) 
    { 
        
string fileName = Path.GetFileName(fullyQualifiedFileName); 

        
uint fileNameLength = (uint)fileName.Length + 1

        
byte[] fileNameBytes = Encoding.Unicode.GetBytes(fileName); 

        
using (BinaryWriter bw = new BinaryWriter(ms)) 
        { 
            
// 写入Infopath文件签名信息 
            bw.Write(new byte[] { 0xC70x490x460x41 }); 

            
// 写入默认的头信息. 
            bw.Write((uint)0x14);    // size 
            bw.Write((uint)0x01);    // version 
            bw.Write((uint)0x00);    // reserved 

            
// 写入文件大小 
            bw.Write((uint)br.BaseStream.Length); 

            
// 写入文件名长度. 
            bw.Write((uint)fileNameLength); 

            
// 写入文件名 (Unicode编码). 
            bw.Write(fileNameBytes); 

            
//写入文件名终止符(两个空字符) 
            bw.Write(new byte[] { 00 }); 

            
//写入文件内容 
            byte[] data = new byte[64 * 1024]; 
            
int bytesRead = 1

            
while (bytesRead > 0
            { 
                bytesRead 
= br.Read(data, 0, data.Length); 
                bw.Write(data, 
0, bytesRead); 
            } 
        } 
    } 

return base64EncodedFile = Convert.ToBase64String(ms.ToArray());



 

最后,请注意,即使在程序写入Infopath表单是没有限制,尽量不要将大文件作为附件附加到Infopath,以免影响性能。此外,Infopath本身,对于附件有很多限制,如下列附加名的文件是不允许作为附件的。同样,在用程序是现实,也没有限制,不过还是请在附件文件是做一个简单的校验,防止通过程序在Infopath表单中加入非法文件。
ade, adp, app, asp, bas, bat, cer, chm, cmd, com, cpl, crt, csh, exe, fxp, hlp, hta, inf, ins, isp, its, js, jse, ksh, lnk, mad, maf, mag, mam, maq, mar, mas, mat, mau, mav, maw, mda, mdb, mde, mdt, mdw, mdz, msc, msi, msp, mst, ops, pcd, pif, prf, prg, pst, reg, scf, scr, sct, shb, shs, tmp, url, vb, vbe, vbs, vsmacros, vss, vst, vsw, ws, wsc, wsf, wsh

posted on 2008-12-30 02:50  greatwave  阅读(1318)  评论(0)    收藏  举报