如何通过编程方式将SharePoint中的Infopath表单及附件进行归档
事情是这样的:我们通过Infopath设计了一些电子表单,用来在企业内部进行一些流程审批的载体。结合SharePoint Server所提供的Forms Service,我们可以比较便捷地实现,在线填写也很方便,如下图所示
在浏览器中填写的效果如下
注意:这只是一个演示表单,我简单做了几个字段而已。重点要体现附件。
那么,现在的需求是这样:因为这些表单越来越多,而且大多有附件,导致SharePoint的内容数据库越来越大,速度受到一定的影响。用户想到一个做法,就是定期将那些已经完成审批的表单归档,而且从这个表单库中删除掉。
在归档的时候,就会遇到一个问题,如何将附件也归档,并且放到指定的磁盘文件夹上去。
首先,我们需要了解的是,Infopath的附件默认是怎么存储的呢?Infopath表单其实就是一份特殊的XML文件,它会将所有的信息,包括附件在内都保存在一个XML文件中。当然,附件会通过编码成Base64的字符串保存。我们可以将之前填写好的这个表单保存下来一份数据,以便了解它里面的结构
这个文件,我们可以用记事本直接打开
大家可以看到,其实附件的内容都是保存在这个xml文件里面的。
通过一些研究,我实现了如下的解决方案,这是一个原型,可以作为一个参考。
【备注】具体在做的时候,还要细致一些将所有数据都妥善保存,本例重点演示如何保存附件。其他常规的数据应该很容易处理。可以读取出来存放在数据库中的一个表中。
下面这个类型是我在网上找到的,不是我的原创。这个类型是一个解码器,可以将上面的Base64String还原为一个字节数组
using System; using System.Collections.Generic; using System.Text; using System.IO; /// <summary> /// Class used to decode an InfoPath attachment. /// Pulls the file name and the decoded file from either a base 64 byte array or string. /// </summary> public class InfoPathAttachmentDecoder { // Private string to hold the attachment name. string _fileName; // Private byte array to hold the decoded attachment. byte[] _decodedFile; /// <summary> /// The name of the file within the InfoPath attachment. /// </summary> public string Filename { get { return _fileName; } } /// <summary> /// The decoded file within the InfoPath attachment. /// </summary> public byte[] DecodedFile { get { return _decodedFile; } } /// <summary> /// Constructor for the InfoPathAttachmentDecoder Class /// </summary> /// <param name="base64EncodedString">The attachment represented by a string</param> public InfoPathAttachmentDecoder(string base64EncodedString) { // Use unicode encoding. Encoding _encoding = Encoding.Unicode; // The byte array containing the data. byte[] _data = Convert.FromBase64String(base64EncodedString); // Use a memory stream to access the data. using(MemoryStream _memoryStream = new MemoryStream(_data)) { // Create a binary reader from the stream. BinaryReader _theReader = new BinaryReader(_memoryStream); // Create a byte array to hold the header data. byte[] _headerData = _theReader.ReadBytes(16); // Find the file size before finding the file name. int _fileSize = (int)_theReader.ReadUInt32(); // Get the file name. int _attachmentNameLength = (int)_theReader.ReadUInt32() * 2; byte[] _fileNameBytes = _theReader.ReadBytes(_attachmentNameLength); _fileName = _encoding.GetString(_fileNameBytes, 0, _attachmentNameLength - 2); // Get the decoded attachment. _decodedFile = _theReader.ReadBytes(_fileSize); } } /// <summary> /// Constructor for the InfoPathAttachmentDecoder Class /// </summary> /// <param name="base64EncodedBytes">The attachment represented by a byte array</param> public InfoPathAttachmentDecoder(byte[] base64EncodedBytes) : this(Convert.ToBase64String(base64EncodedBytes)) { } /// <summary> /// Static method that gets the file from the attachment. /// </summary> /// <param name="base64EncodedString">The attachment represented by a string</param> /// <returns>Returns a byte array of the file in the attachment.</returns> public static byte[] DecodeInfoPathAttachment(string base64EncodedString) { // Create an instance of the InfoPathAttachmentDecoder InfoPathAttachmentDecoder _infoPathAttachmentDecoder = new InfoPathAttachmentDecoder(base64EncodedString); // Return the decoded file. return _infoPathAttachmentDecoder.DecodedFile; } /// <summary> /// Static method that gets the file from the attachment. /// </summary> /// <param name="base64EncodedBytes">The attachment represented by a byte array</param> /// <returns>Returns a byte array of the file in the attachment.</returns> public static byte[] DecodeInfoPathAttachment(byte[] base64EncodedBytes) { // Create an instance of the InfoPathAttachmentDecoder InfoPathAttachmentDecoder _infoPathAttachmentDecoder = new InfoPathAttachmentDecoder(base64EncodedBytes); // Return the decoded file. return _infoPathAttachmentDecoder.DecodedFile; } }
然后,我们需要实现对XML文档的读取。虽然读XML文件向来都不是什么大问题,但Infopath的XML文档结构还是挺繁琐的,有很多命名空间,直接读取相当费时费力。我一般会用下面的方式
1. 打开Visual Studio Command Prompt
2. 根据xml文件生成xsd(架构)
3.根据xsd文件生成一个强类型的class
准备工作做好了,下面我们做一个简单的程序来实现一下整个存档的逻辑
1. 创建一个Windows Forms程序
【注意】选择.NET Framework 3.5
2. 设置编译平台为x64(这是访问SharePoint服务器对象模型的要求)
3. 做一个简单的界面如下
4. 引用Microsoft.SharePoint.dll
5. 将之前生成好的10248.cs和写好的InfoPathAttachmentDecoder 类型添加到项目中来
6.编写代码
using System; using System.IO; using System.Linq; using System.Windows.Forms; using System.Xml.Serialization; using Microsoft.SharePoint; namespace FormArchiver { public partial class MainForm : Form { public MainForm() { InitializeComponent(); } private void btStart_Click(object sender, EventArgs e) { var path = txtLibPath.Text; var folder = txtFolder.Text; var site = new SPSite(path); var web = site.OpenWeb(); var list = web.GetList(path); var items = list.Items; foreach(SPListItem item in items) { var file = item.File; var stream = file.OpenBinaryStream(); var serializer = new XmlSerializer(typeof(myFields));//这里的myFields这个类型,是之前通过xsd工具根据表单结构生成的 var result =(myFields) serializer.Deserialize(stream); var attachment = new InfoPathAttachmentDecoder(result.group3.FirstOrDefault().field4); var fileName = attachment.Filename; var buffer = attachment.DecodedFile; if(!Directory.Exists(folder)) Directory.CreateDirectory(folder); var targetPath = Path.Combine(folder, fileName); File.WriteAllBytes(targetPath, buffer); } MessageBox.Show("保存完成"); } } }
这个程序运行起来的效果大致如下
点击“开始”,很快的我们就可以将附件保存出来到预设的目录
这个演示程序的源代码,请通过这里下载