如何在 InfoPath 2010 或 InfoPath 2007 中使用 Visual C# 以编程方式对文件附件进行编码和解码

摘要

在 Microsoft InfoPath 2010 或 Microsoft Office InfoPath 2007 中,可以使用文件附件控件将文件附加到 InfoPath 表单模板。 在特定情况下,可能需要进行编码,然后解码附加到文件附件控件的文件。 在这种情况下,可以使用 Microsoft Visual C# 创建编码器类和解码器类。 然后,可以使用 Encoderclass 和 Decoderclass 对文件附件进行编码和解码。 本文介绍如何设计 InfoPath 表单模板,该模板将文件编码为窗体附件,并解码用于保存到文件系统的附件。  

有关如何在 InfoPath 2003 中执行此操作的详细信息,请单击以下文章编号以查看 Microsoft 知识库中的文章:

892730 如何使用 InfoPath 中的 Visual C# 以编程方式对文件附件进行编码和解码

更多信息

Microsoft 提供的编程示例仅供演示,不提供明示或暗示的担保,包括但不限于对适销性和/或特定用途适用性的暗示担保。 本文假设你熟悉所演示的编程语言以及用于创建和调试过程的工具。 Microsoft 支持专业人员可以帮助解释特定过程的功能,但他们不会修改这些示例以提供新增功能或构建步骤以满足你的特定需要。

在以下示例中,你将执行以下步骤:

  1. 创建包含 Visual C# 项目的窗体。
  2. 创建 Encoderclass。
  3. 创建解码器类。
  4. 修改表单以调用代码。
  5. 测试窗体中的代码操作。

创建包含 Visual C# 项目的窗体

使用所使用的 InfoPath 版本的说明创建表单模板和项目。

InfoPath 2010

  1. 在 InfoPath 设计器中,创建空白表单模板,然后单击“保存”图标。
  2. 系统提示时,键入文件名,然后单击“保存”。
  3. 单击“开发人员”选项卡,然后单击“语言”。
  4. 在窗体模板代码语言下,选择 C#,然后单击“确定”。

InfoPath 2007

  1. 在“文件”菜单上,单击 “设计窗体模板”。
  2. 在“设计窗体模板”“任务窗格中,单击 ”空白 “,然后单击 ”确定”。
  3. 在“文件”菜单上,单击“保存”
  4. 出现提示时,键入文件名 InfoPathAttachmentEncoding,然后单击 “保存”。
  5. 在“工具”菜单上,单击“表单选项”
  6. 在“窗体选项”对话框中的 “类别 ”下,单击 “编程”。
  7. “编程语言”下,在 窗体模板代码语言 列表中,选择 C#,然后单击 “确定”。

创建 Encoder 类

使用所使用的 InfoPath 版本的说明创建 Encoderclass。

InfoPath 2010

  1. “开发人员”选项卡下,单击“代码编辑器”以启动VISUAL STUDIO TOOLS FOR APPLICATIONS (VSTA) 编辑器。
  2. “项目” 菜单中,单击 “添加新项”。
  3. 单击此选项可选择 “类”。
  4. “名称” 字段中,将名称更改为 InfoPathAttachmentEncoder.cs,然后单击“ 保存”。
  5. 在代码窗口中,将现有代码替换为以下“编码器代码”部分中的代码。

InfoPath 2007

  1. 启动VISUAL STUDIO TOOLS FOR APPLICATIONS (VSTA) 编辑器。 
  2. 在“项目资源管理器”窗格中,右键单击 InfoPathAttachmentEncoding,单击 “添加”,然后单击 “新建项”。
  3. “模板” 部分中,选择 “类”,将名称更改为 InfoPathAttachmentEncoder.cs,然后单击 “添加”。
  4. 在代码窗口中,粘贴以下“编码器代码”部分中的代码。

编码器代码

在 InfoPath 2010 或 InfoPath 2007 中使用以下代码:

using System;
using System.IO;
using System.Text;
using System.Security.Cryptography;
using InfoPathAttachmentEncoding;

namespace InfoPathAttachmentEncoding
{
/// <summary>
/// InfoPathAttachment encodes file data into the format expected by InfoPath for use in file attachment nodes.
/// </summary>
public class InfoPathAttachmentEncoder
{
private string base64EncodedFile = string.Empty;
private string fullyQualifiedFileName;

/// <summary>
/// Creates an encoder to create an InfoPath attachment string.
/// </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>
/// Returns a Base64 encoded string.
/// </summary>
/// <returns>String</returns>
public string ToBase64String()
{
if (base64EncodedFile != string.Empty)
return base64EncodedFile;

// This memory stream will hold the InfoPath file attachment buffer before Base64 encoding.
MemoryStream ms = new MemoryStream();

// Obtain the file information.
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))
{
// Write the InfoPath attachment signature. 
bw.Write(new byte[] { 0xC7, 0x49, 0x46, 0x41 });

// Write the default header information.
bw.Write((uint)0x14);// size
bw.Write((uint)0x01);// version
bw.Write((uint)0x00);// reserved

// Write the file size.
bw.Write((uint)br.BaseStream.Length);

// Write the size of the file name.
bw.Write((uint)fileNameLength);

// Write the file name (Unicode encoded).
bw.Write(fileNameBytes);

// Write the file name terminator. This is two nulls in Unicode.
bw.Write(new byte[] {0,0});

// Iterate through the file reading data and writing it to the outbuffer.
byte[] data = new byte[64*1024];
int bytesRead = 1;

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

// This memorystream will hold the Base64 encoded InfoPath attachment.
MemoryStream msOut = new MemoryStream();

using (BinaryReader br = new BinaryReader(new MemoryStream(ms.ToArray())))
{
// Create a Base64 transform to do the encoding.
ToBase64Transform tf = new ToBase64Transform();

byte[] data = new byte[tf.InputBlockSize];
byte[] outData = new byte[tf.OutputBlockSize];

int bytesRead = 1;

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

if (bytesRead == data.Length)
tf.TransformBlock(data, 0, bytesRead, outData, 0);
else
outData = tf.TransformFinalBlock(data, 0, bytesRead);

msOut.Write(outData, 0, outData.Length);
}
}

msOut.Close();

return base64EncodedFile = Encoding.ASCII.GetString(msOut.ToArray());
}
}
}

创建解码器类

使用所使用的 InfoPath 版本的说明创建解码器类。

InfoPath 2010

  1. 在代码编辑器的 “项目 ”菜单中,单击 “添加新项”,然后单击以选择 “类”。
  2. “名称” 字段中,将名称更改为 InfoPathAttachmentDecoder.cs,然后单击“ 保存”。
  3. 在代码窗口中,将现有代码替换为以下“解码器代码”部分中的代码。 

InfoPath 2007

  1. 在 VSTA 中的“项目资源管理器”窗格中,右键单击 InfoPathAttachmentEncoding,单击 “添加”,然后单击 “新建项”。
  2. “模板” 部分中,选择 “类”,将名称更改为 InfoPathAttachmentDecoder.cs,然后单击 “添加”。
  3. 在代码窗口中,粘贴以下“解码器代码”部分中的代码。

解码器代码

在 InfoPath 2010 或 InfoPath 2007 中使用以下代码:

using System;
using System.IO;
using System.Text;

namespace InfoPathAttachmentEncoding
{
/// <summary>
/// Decodes a file attachment and saves it to a specified path.
/// </summary>
public class InfoPathAttachmentDecoder
{
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>
/// Accepts the Base64 encoded string
/// that is the attachment.
/// </summary>
public InfoPathAttachmentDecoder(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)
{
//Position the reader to obtain the file size.
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 uses UTF8 encoding.
Encoding enc = Encoding.Unicode;
attachmentName = enc.GetString(fileNameBytes, 0, attachmentNameLength - 2);
decodedAttachment = theReader.ReadBytes(fileSize);
}

public void SaveAttachment(string saveLocation)
{
string fullFileName = saveLocation;
if (!fullFileName.EndsWith(Path.DirectorySeparatorChar.ToString())){
fullFileName += Path.DirectorySeparatorChar;
}

fullFileName += attachmentName;

if(File.Exists(fullFileName))
File.Delete(fullFileName);

FileStream fs = new FileStream(fullFileName, FileMode.CreateNew);
BinaryWriter bw = new BinaryWriter(fs);
bw.Write(decodedAttachment);

bw.Close();
fs.Close();
}

public string Filename
{
get{ return attachmentName; }
}

public byte[] DecodedAttachment
{
get{ return decodedAttachment; }
}
}
}

修改窗体

将文件附件控件、文本框控件和按钮控件添加到 InfoPath 窗体。 为此,请按照下列步骤操作:

  1. 使用所使用的 InfoPath 版本的说明打开“ 控件 ”任务窗格。 

    • InfoPath 2010:在 InfoPathAttachmentEncoding InfoPath 表单模板中的“主页”选项卡下展开“控件”库。
    • InfoPath 2007:在 InfoPathAttachmentEncoding InfoPath 表单模板中,单击“设计任务”**任务窗格中的 件。
  2. 使用所使用的 InfoPath 版本的说明将文件附件控件添加到 InfoPath 表单:

    • InfoPath 2010:在 “控件 ”任务窗格中的 “对象”下,单击 “文件附件”。
    • InfoPath 2007:在 “控件 ”任务窗格的 “文件和图片”下,单击 “文件附件”。
  3. 右键单击“文件附件”控件,然后单击 “文件附件属性”。

  4. 在“文件附件属性”对话框中,键入“字段名称”框中的AttachmentField,然后单击 “确定”。

  5. 使用所使用的 InfoPath 版本的说明将 Text Box 控件添加到 InfoPath 窗体:

    • InfoPath 2010:在 “控件 ”任务窗格的“ 输入”下,单击 “文本框”。
    • InfoPath 2007:在 “控件 ”任务窗格中的 “标准”下,单击 “文本框”。
  6. 右键单击“文本框”控件,然后单击 “文本框属性”。

  7. 在“文本框属性”对话框中,在“字段名称”框中键入AttachmentName,然后单击 “确定”。

  8. 使用所使用的 InfoPath 版本的说明将“附加”按钮添加到 InfoPath 窗体:

    • InfoPath 2010:在 “控件 ”任务窗格中的 “对象”下,单击 “按钮”。
    • InfoPath 2007:在 “控件 ”任务窗格中的 “标准”下,单击 “按钮”。
  9. 右键单击新按钮控件,然后单击 “按钮属性”。

  10. 在“按钮属性”对话框中,键入“标签”框中的“附加”,在 ID 框中键入 btnAttach,然后单击 “编辑表单代码”。

  11. 移动到代码窗口的顶部,然后添加以下代码行:

    using InfoPathAttachmentEncoding;
    
  12. 返回“在此处编写代码”,然后添加以下代码:

     //Create an XmlNamespaceManager
     XmlNamespaceManager ns = this.NamespaceManager;
    
    //Create an XPathNavigator object for the Main data source
     XPathNavigator xnMain = this.MainDataSource.CreateNavigator();
    
    //Create an XPathNavigator object for the attachment node
     XPathNavigator xnAttNode = xnMain.SelectSingleNode("/my:myFields/my:theAttachmentField", ns);
    
    //Create an XPathNavigator object for the filename node
     XPathNavigator xnFileName = xnMain.SelectSingleNode("/my:myFields/my:theAttachmentName", ns);
    
    //Obtain the text of the filename node.
     string fileName = xnFileName.Value;
     if (fileName.Length > 0)
     {
      //Encode the file and assign it to the attachment node.
      InfoPathAttachmentEncoder myEncoder = new InfoPathAttachmentEncoder(fileName);
    
    //Check for the "xsi:nil" attribute on the file attachment node and remove it
      //before setting the value to attach the filerRemove the "nil" attribute
      if (xnAttNode.MoveToAttribute("nil", "http://www.w3.org/2001/XMLSchema-instance"))
       xnAttNode.DeleteSelf();
    
    //Attach the file
      xnAttNode.SetValue(myEncoder.ToBase64String());
     }              
    
    

将“保存”按钮添加到 InfoPath 窗体

  1. 返回到 InfoPathAttachmentEncoding 表单模板。

  2. 使用所使用的 InfoPath 版本的说明添加“保存”按钮:

    • InfoPath 2010:在 “控件 ”任务窗格中的 “对象”下,单击 “按钮”。
    • InfoPath 2007:在 “控件 ”任务窗格中的 “标准”下,单击 “按钮”。
  3. 右键单击新按钮控件,然后单击 “按钮属性”。

  4. 在“按钮属性”对话框中,键入“标签”框中的“保存”,在“ID”框中键入 btnSave,然后单击 “编辑表单代码”。

  5. 插入以下代码:

     //Create an XmlNamespaceManager
     XmlNamespaceManager ns = this.NamespaceManager;
    
    //Create an XPathNavigator object for the Main data source
     XPathNavigator xnMain = this.MainDataSource.CreateNavigator();
    
    //Create an XPathNavigator object for the attachment node
     XPathNavigator xnAttNode = xnMain.SelectSingleNode("/my:myFields/my:theAttachmentField", ns);
    
    //Obtain the text of the node.
     string theAttachment = xnAttNode.Value;
     if(theAttachment.Length > 0)
     {
         InfoPathAttachmentDecoder myDecoder = new InfoPathAttachmentDecoder(theAttachment);
         myDecoder.SaveAttachment(@"Path to the folder to save the attachment");
     }                                              
    
    

注意

在此代码中,用于保存附件的文件夹的占位符路径表示要保存文件的位置。

单击“保存”图标,然后关闭 VSTA。

测试窗体

在此表单可以附加文件之前,必须完全信任 InfoPath 表单模板。 若要确保这一点,请执行以下操作之一:

  • 使用代码签名证书对表单模板文件 (.xsn) 进行数字签名。 执行此操作时,系统会提示用户在打开窗体时信任表单。 这使得表单完全受信任。 因此,向 Visual C# 代码授予完全信任权限。
  • 创建可安装模板。

确保表单模板完全受信任后,应对其进行测试。 为此,请按照下列步骤操作:

  1. 按照所使用的 InfoPath 版本的步骤,在预览模式下打开窗体:

    • InfoPath 2010“开始”选项卡下,单击“预览”。
    • InfoPath 2007 在 Standardtoolbar 上,单击“预览”。
  2. 在 InfoPath 窗体中,键入要在文本框中附加的文件的路径,然后单击 “附加”。

    注意 双击“文件附件”控件以验证文件是否已正确编码。 

  3. 单击“保存”

  4. 找到在“向 InfoPath 窗体添加保存按钮”部分的代码中提供的路径,然后确保文件已保存到该文件夹。 

  5. 单击“关闭预览”。 这会结束测试。