详解如何用程序读写Infopath附件

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


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

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

      

       读取附件的代码部分:

  1. private const int SP1Header_Size = 20; 
  2. private const int FIXED_HEADER = 16; 
  3. private int fileSize; 
  4. private int attachmentNameLength; 
  5. private string attachmentName; 
  6. private byte[] decodedAttachment; 
  7. /// 
  8. /// 解码基于Base64编码的Infopath附件字符串, 
  9. ///并将解码后的文件名和文件内容保存为内部私有变量 
  10. /// 
  11. /// Base64编码的字符串 
  12. public void InfoPathAttachmentDecode(string theBase64EncodedString) 
  13.     byte[] theData = Convert.FromBase64String(theBase64EncodedString); 
  14.     using (MemoryStream ms = new MemoryStream(theData)) 
  15.     { 
  16.         BinaryReader theReader = new BinaryReader(ms); 
  17.         DecodeAttachment(theReader); 
  18.     } 
  19. private void DecodeAttachment(BinaryReader theReader) 
  20.     //获取头部结构数据 
  21.     byte[] headerData = new byte[FIXED_HEADER]; 
  22.     headerData = theReader.ReadBytes(headerData.Length); 
  23.     fileSize = (int)theReader.ReadUInt32(); 
  24.     attachmentNameLength = (int)theReader.ReadUInt32() * 2; 
  25. //获取文件名流 
  26.     byte[] fileNameBytes = theReader.ReadBytes(attachmentNameLength); 
  27.     //InfoPath 使用的是UTF8编码,将文件名转换为UTF8字符串 
  28.     Encoding enc = Encoding.Unicode; 
  29.     attachmentName = enc.GetString(fileNameBytes, 0, attachmentNameLength - 2); 
  30.     //获取附件内容,并将其保存为二进制数组 
  31.     decodedAttachment = theReader.ReadBytes(fileSize); 
  32. }

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

 

  1. private string base64EncodedFile = string.Empty; 
  2. private string fullyQualifiedFileName; 
  3. /// 
  4. /// 将指定文件编码成Infopath可识别Base64字符串 
  5. /// 
  6. /// 文件全路径 
  7. public InfoPathAttachmentEncoder(string fullyQualifiedFileName) 
  8.     if (fullyQualifiedFileName == string.Empty) 
  9.         throw new ArgumentException("Must specify file name""fullyQualifiedFileName"); 
  10.     if (!File.Exists(fullyQualifiedFileName)) 
  11.         throw new FileNotFoundException("File does not exist: " + fullyQualifiedFileName, fullyQualifiedFileName); 
  12.     this.fullyQualifiedFileName = fullyQualifiedFileName; 
  13. /// 
  14. /// 获取Based64编码字串. 
  15. /// 
  16. /// String 
  17. public string ToBase64String() 
  18.     if (base64EncodedFile != string.Empty) 
  19.         return base64EncodedFile; 
  20.     // MemoryStream将用于保存文件附件数据流 
  21.     MemoryStream ms = new MemoryStream(); 
  22.     // 获取文件信息. 
  23.     using (BinaryReader br = new BinaryReader(File.Open(fullyQualifiedFileName, FileMode.Open, FileAccess.Read, FileShare.Read))) 
  24.     { 
  25.         string fileName = Path.GetFileName(fullyQualifiedFileName); 
  26.         uint fileNameLength = (uint)fileName.Length + 1; 
  27.         byte[] fileNameBytes = Encoding.Unicode.GetBytes(fileName); 
  28.         using (BinaryWriter bw = new BinaryWriter(ms)) 
  29.         { 
  30.             // 写入Infopath文件签名信息 
  31.             bw.Write(new byte[] { 0xC7, 0x49, 0x46, 0x41 }); 
  32.             // 写入默认的头信息. 
  33.             bw.Write((uint)0x14);    // size 
  34.             bw.Write((uint)0x01);    // version 
  35.             bw.Write((uint)0x00);    // reserved 
  36.             // 写入文件大小 
  37.             bw.Write((uint)br.BaseStream.Length); 
  38.             // 写入文件名长度. 
  39.             bw.Write((uint)fileNameLength); 
  40.             // 写入文件名 (Unicode编码). 
  41.             bw.Write(fileNameBytes); 
  42.             //写入文件名终止符(两个空字符) 
  43.             bw.Write(new byte[] { 0, 0 }); 
  44.             //写入文件内容 
  45.             byte[] data = new byte[64 * 1024]; 
  46.             int bytesRead = 1; 
  47.             while (bytesRead > 0) 
  48.             { 
  49.                 bytesRead = br.Read(data, 0, data.Length); 
  50.                 bw.Write(data, 0, bytesRead); 
  51.             } 
  52.         } 
  53.     } 
  54. 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

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值