Dot-Net

從 Access 數據庫中的附件欄位中提取文件

  • June 7, 2021

我們正在進行一個項目,我們需要將儲存在 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();

(我對這個問題的原始回答具有誤導性。它適用於隨後使用 Adob​​e Reader 打開的 PDF 文件,但它並不總是適用於其他類型的文件。以下是更正後的版本。)

不幸的是,我們無法Attachment使用 OleDb 直接檢索 Access 欄位中的文件內容。.FileDataAccess 數據庫引擎將一些元數據添加到文件的二進制內容中,如果我們通過 OleDb檢索該元數據,則會包含該元數據。

為了說明,使用 Access UI 將名為“Document1.pdf”的文件保存到附件欄位。該 PDF 文件的開頭如下所示:

原創.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();
       }
   }
}

然後生成的文件將包含文件開頭的元數據(在這種情況下為 20 個字節)

FromFileData.png

Adobe Reader 能夠打開此文件,因為它足夠強大,可以忽略文件中可能出現在“%PDF-1.4”簽名之前的任何“垃圾”。不幸的是,並非所有文件格式和應用程序都對文件開頭的無關字節如此寬容。

從Access 中的欄位中提取文件的唯一Official™ 方法是使用ACE DAO對象的方法,如下所示:Attachment``.SaveToFile``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();

請注意,如果您嘗試使用.ValueField2 對象,您仍然會在字節序列的開頭獲得元數據;這個.SaveToFile過程就是把它剝離出來。

引用自:https://stackoverflow.com/questions/25864092