从Access数据库的"附件"字段中提取文件

ccS*_*ars 6 .net database ms-access

我们正在开发一个项目,我们需要将存储在Access数据库中的数据迁移到缓存数据库.Access数据库包含数据类型为的列Attachment; 一些元组包含多个附件.我可以通过使用获取这些文件的文件名.FileName,但我不确定如何确定一个文件何时结束而另一个文件何时开始.FileData.

我使用以下内容来获取此数据:

System.Data.OleDb.OleDbCommand command= new System.Data.OleDb.OleDbCommand();
command.CommandText = "select [Sheet1].[pdf].FileData,* from [Sheet1]";
command.Connection = conn;
System.Data.OleDb.OleDbDataReader rdr = command.ExecuteReader();
Run Code Online (Sandbox Code Playgroud)

Gor*_*son 9

(我对这个问题的原始答案是误导性的.对于随后使用Adobe Reader打开的PDF文件,它可以正常工作,但对于其他类型的文件,它并不总是正常工作.以下是更正后的版本.)

遗憾的是,我们无法Attachment使用OleDb 直接检索Access 字段中文件的内容.Access数据库引擎会将一些元数据预先添加到文件的二进制内容中,如果我们检索.FileDatavia OleDb ,则会包含元数据.

为了说明,使用Access UI将名为"Document1.pdf"的文档保存到"附件"字段.该PDF文件的开头如下所示:

Original.png

如果我们使用以下代码尝试将PDF文件解压缩到磁盘

using (OleDbCommand cmd = new OleDbCommand())
{
    cmd.Connection = con;
    cmd.CommandText = 
            "SELECT Attachments.FileData " +
            "FROM AttachTest " +
            "WHERE Attachments.FileName='Document1.pdf'";
    using (OleDbDataReader rdr = cmd.ExecuteReader())
    {
        rdr.Read();
        byte[] fileData = (byte[])rdr[0];
        using (var fs = new FileStream(
                @"C:\Users\Gord\Desktop\FromFileData.pdf", 
                FileMode.Create, FileAccess.Write))
        {
            fs.Write(fileData, 0, fileData.Length);
            fs.Close();
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

然后生成的文件将包含文件开头的元数据(在这种情况下为20个字节)

FromFileData.png

Adobe Reader能够打开此文件,因为它足够强大,可以忽略在'%PDF-1.4'签名之前可能出现在文件中的任何"垃圾".遗憾的是,并非所有文件格式和应用程序都对文件开头的无关字节容忍.

从Access中的字段中提取文件的唯一官方方法Attachment是使用.SaveToFileACE DAO Field2对象的方法,如下所示:

// required COM reference: Microsoft Office 14.0 Access Database Engine Object Library
//
// using Microsoft.Office.Interop.Access.Dao; ...
var dbe = new DBEngine();
Database db = dbe.OpenDatabase(@"C:\Users\Public\Database1.accdb");
Recordset rstMain = db.OpenRecordset(
        "SELECT Attachments FROM AttachTest WHERE ID=1",
        RecordsetTypeEnum.dbOpenSnapshot);
Recordset2 rstAttach = rstMain.Fields["Attachments"].Value;
while ((!"Document1.pdf".Equals(rstAttach.Fields["FileName"].Value)) && (!rstAttach.EOF))
{
    rstAttach.MoveNext();
}
if (rstAttach.EOF)
{
    Console.WriteLine("Not found.");
}
else
{
    Field2 fld = (Field2)rstAttach.Fields["FileData"];
    fld.SaveToFile(@"C:\Users\Gord\Desktop\FromSaveToFile.pdf");
}
db.Close();
Run Code Online (Sandbox Code Playgroud)

请注意,如果您尝试使用.ValueField2对象,您仍将在字节序列的开头获取元数据; 这个.SaveToFile过程就是把它剥离出来的.