2014-09-16 13 views
6

Access veritabanında depolanan verileri bir önbellek veritabanına geçirmemiz gereken bir proje üzerinde çalışıyoruz. Access veritabanı, Attachment veri türüne sahip sütunlar içerir; Bazı tupller çoklu ekler içerir. Bu dosyaların dosya adlarını .FileName kullanarak edinebilirim, ancak bir dosyanın ne zaman bittiğini ve .FileData başladığında nasıl belirleyeceğinden emin değilim.Access veritabanındaki bir Ek alanından dosyaları ayıklama

ben bu verileri elde etmek için aşağıdaki kullanıyorum:

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(); 

cevap

8

(Benim bu soruya asıl cevap yanıltıcı olduğunu Sonradan Adobe Reader ile açıldı PDF dosyaları için tamam çalıştı, ama öyle olmadı. her zaman diğer dosya türleri için uygun şekilde çalışır. Aşağıdaki düzeltme sürümüdür.)

Maalesef, bir dosyanın içeriğini doğrudan Access Attachment alanında OleDb kullanarak alamıyoruz. Access Veritabanı Altyapısı, dosyanın ikili içeriğine bazı meta verileri ekler ve .FileData'u OleDb aracılığıyla alırsak meta veriler eklenir.

Örnek vermek gerekirse, "Document1.pdf" adlı bir belge Erişim Arayüzü kullanılarak bir Ek alanına kaydedilir. Bu PDF dosyası başlangıcı şuna benzer:

Original.png

biz denemek ve diske

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

PDF dosyasını ayıklamak için aşağıdaki kodu kullanmak istersek sonuç dosyası içerecektir dosyanın başında meta (bu durumda 20 bayt)

FromFileData.png

Adobe Reader, bu dosyayı açabilir çünkü '% PDF-1.4' imzası öncesinde dosyada görünebilecek herhangi bir "önemsiz" öğesini yok saymaya yetecek kadar sağlamdır. Ne yazık ki, tüm dosya formatları ve uygulamaları, dosyanın başında gereksiz baytlar için çok bağışlayıcı değildir.

Access'te bir Attachment sahadan dosyaların ayıklanması sadece Resmi ™ yolu, bir ACE DAO Field2 nesnenin .SaveToFile yöntemi kullanmak şöyle etmektir

:

// 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(); 

Not Kullandığınız denerseniz Field2 nesnesinin .Value, hala bayt dizisinin başında meta verileri alacaktır; .SaveToFile işlemi, onu dışarı çıkaran işlemdir.

// Bu eki alan içinde depolanan bir dosya almak için bilgi parçalarını bir araya biraz zaman aldı

+0

Çok ilginç, böyle bir şey arıyordum ve bulamıyorum. – Steve

+1

Gord, sen adamsın. Teşekkürler. –

+0

Ekler sütunundan bir dizi dosya nasıl çıkarılabilir? –

0

// Merhaba, bu yüzden sadece id pay düşündüm.

using System; 
using System.Collections.Generic; 
using System.ComponentModel; 
using System.Data; 
using System.Drawing; 
using System.Linq; 
using System.Text; 
using System.Windows.Forms; 
using System.Data.OleDb; 
using System.IO; 
using System.Diagnostics; 

namespace AttachCheck 
{ 
public partial class Form1 : Form 
{ 
    DataSet Set1 = new DataSet(); 
    int ColId; 

    public Form1() 
    { 
     InitializeComponent(); 

     OleDbConnection connect = new OleDbConnection("Provider=Microsoft.ACE.OLEDB.12.0;Data Source='db/Adb.accdb'"); //set up connection 
     //CL_ID is a fk so attachments can be linked to users 
     OleDbCommand sql = new OleDbCommand("SELECT at_ID, [at_Name].[FileData], [at_Name].[FileName], [at_Name].[FileType] FROM Attachments WHERE at_ID =1;", connect); 
     //adding sql to addapter to be ran 

     OleDbDataAdapter OleDA = new OleDbDataAdapter(sql); 
     //attempting to open connection 
     try { connect.Open(); } 
     catch (Exception err) { System.Console.WriteLine(err); } 


     OleDA.Fill(Set1); //create and fill dataset 
     connect.Close();for (int i = 0; i < Set1.Tables[0].Rows.Count; i++) 
     { 
      System.Console.WriteLine(Set1.Tables[0].Rows[i]["at_Name.FileName"].ToString() + "This is the file name"); 


     // by using a datagrid it allows you to display the attachments and select which to open, the open should be a button. 
     dataGridView1.Rows.Add(new object[] { Set1.Tables[0].Rows[i]["at_ID"].ToString(), Set1.Tables[0].Rows[i]["at_Name.FileName"].ToString(), "Open" }); 
     } 
    } 

    private void dataGridView1_CellContentClick(object sender, DataGridViewCellEventArgs e) 
    { 

     DataGridViewCell cell = (DataGridViewCell) 
     dataGridView1.Rows[e.RowIndex].Cells[e.ColumnIndex]; 

     System.Console.WriteLine(dataGridView1.Rows[e.RowIndex].Cells[e.ColumnIndex]); 
     string FullRow = dataGridView1.Rows[e.RowIndex].ToString(); //data retrieved from click on datagrid 
     //need to sub string to cut away row index and leave number 
     string SubRow = FullRow.Substring(24, 1); //cutting string down from position 24 for 1 character 

     System.Console.WriteLine(SubRow + " This is Row"); // 

     int RowId = int.Parse(SubRow); //turn row number from string into integer that can be used 

     string FullRow2 = dataGridView1.Rows[e.RowIndex].Cells[e.ColumnIndex].ToString(); //data retrieved from click on datagrid 
     //need to sub string to cut away row index and leave number 
     string SubRow2 = FullRow2.Substring(37, 1); //cutting string down from position 24 for 1 character 
     System.Console.WriteLine(SubRow2 + " This is Column"); // 
     int ColId = int.Parse(SubRow2); //turn row number from string into integer that can be used 


     if (ColId == 2) 
     { 
      string fileName = Set1.Tables[0].Rows[RowId]["at_Name.FileName"].ToString(); //assign the file to variable 

      //retrieving the file contents from the database as an array of bytes 
      byte[] fileContents = (byte[])Set1.Tables[0].Rows[RowId]["at_Name.FileData"]; 


      fileContents = GetFileContents(fileContents); //send filecontents array to be decrypted 

      string fileType = Set1.Tables[0].Rows[RowId]["at_Name.FileType"].ToString(); 


      DisplayTempFile(fileName, fileContents, fileType); //forward the file type to display file contents 
     } 
    } 

    private const int CONTENT_START_INDEX_DATA_OFFSET = 0; //values used for decoding 
    private const int UNKNOWN_DATA_OFFSET = 4; //the files 
    private const int EXTENSION_LENGTH_DATA_OFFSET = 8; //storedw within the access database 
    private const int EXTENSION_DATA_OFFSET = 12; //and this one 


    private byte[] GetFileContents(byte[] fileContents) 
    { 

     int contentStartIndex = BitConverter.ToInt32(fileContents, CONTENT_START_INDEX_DATA_OFFSET); 

     //'The next four bytes represent a value whose meaning is unknown at this stage, although it may represent a Boolean value indicating whether the data is compressed or not. 
     int unknown = BitConverter.ToInt32(fileContents, UNKNOWN_DATA_OFFSET); 

     //'The next four bytes contain the the length, in characters, of the file extension. 
     int extensionLength = BitConverter.ToInt32(fileContents, EXTENSION_LENGTH_DATA_OFFSET); 

     //'The next field in the header is the file extension, not including a dot but including a null terminator. 
     //'Characters are Unicode so double the character count to get the byte count. 
     string extension = Encoding.Unicode.GetString(fileContents, EXTENSION_DATA_OFFSET, extensionLength * 2); 
     return fileContents.Skip(contentStartIndex).ToArray(); 


    } 


    private void DisplayTempFile(string fileName, byte[] fileContents, string fileType) 
    { 

     // System.Console.WriteLine(fileName + "File Name"); 
     // System.Console.WriteLine(fileType + "File Type"); 
     // System.Console.WriteLine(fileContents + "File Contents"); 

     string tempFolderPath = Path.GetTempPath(); //creating a temperary path for file to be opened from 
     string tempFilePath = Path.Combine(tempFolderPath, fileName); // assigning the file to the path 

     if (!string.IsNullOrEmpty(tempFilePath)) //checking the temp file exists 
     { 
      tempFilePath = Path.Combine(tempFolderPath, //combines the strings 0 and 1 below 
      String.Format("{0}{1}", 
      Path.GetFileNameWithoutExtension(fileName),  //0              
      Path.GetExtension(fileName))); //1 
     } 

     //System.Console.WriteLine(tempFolderPath + " tempFolderPath"); 
     //System.Console.WriteLine(tempFilePath + " tempFilePath"); 

     //'Save the file and open it. 
     File.WriteAllBytes(tempFilePath, fileContents); 
     //creates new file, writes bytes array to it then closes the file 
     //File.ReadAllBytes(tempFilePath); 

     //'Open the file. 
     System.Diagnostics.Process attachmentProcess = Process.Start(tempFilePath); 
     //chooses the program to open the file if available on the computer 

    } 
} 

}

// Umut bu birilerine

0

Aşağıdaki kod, Microsoft Access veritabanı veri tablonun tüm kayıtları geçer ve bir kayıt kümesine her satır atar yardımcı olur. "Dokümanlar" alanına kaydedilen tüm ekler üzerinden geçer. Daha sonra bu dosyaları diske ayıklar ve kaydeder. Bu kod, yukarıdaki "Gord Thompson" tarafından tanıtılan kodun bir uzantısıdır. Yaptığım tek şey, Visual Basic.NET kodunu yazdığımdı.

Imports Microsoft.Office.Interop.Access.Dao 

Yukarıdaki kod satırını kullanarak Dao'ya bir başvuru yapın.

'Visual Basic.NET 
Private Sub ReadAttachmentFiles() 
    'required COM reference: Microsoft Office 14.0 Access Database Engine Object Library 
    'define a new database engine and a new database 
    Dim dbe = New DBEngine 
    Dim db As Database = dbe.OpenDatabase("C:\Users\Meisam\Documents\Databases\myDatabase.accdb") 
    'define the main recordset object for each row 
    Dim rstMain As Recordset = db.OpenRecordset(_ 
      "SELECT * FROM Companies", _ 
      RecordsetTypeEnum.dbOpenSnapshot) 
    'evaluate whether the recordset is empty of records 
    If Not (rstMain.BOF And rstMain.EOF) Then 
     'if not empty, then move to the first record 
     rstMain.MoveFirst() 
     'do until the end of recordset is not reached 
     Do Until rstMain.EOF 
      Dim myID As Integer = -1 
      ' ID is the name of primary field with uniqe values field 
      myID = CInt(rstMain.Fields("ID").Value) 
      'define the secondary recordset object for the attachment field "Docs" 
      Dim rstAttach As Recordset2 = rstMain.Fields("Docs").Value 
      'evaluate whether the recordset is empty of records 
      If Not (rstAttach.BOF And rstAttach.EOF) Then 
       'if not empty, then move to the first record 
       rstAttach.MoveFirst() 
       'do until the end of recordset is not reached 
       Do Until rstAttach.EOF 
        'get the filename for each attachment in the field "Docs" 
        Dim fileName As String = rstAttach.Fields("FileName").Value 
        Dim fld As Field2 = rstAttach.Fields("FileData") 
        fld.SaveToFile("C:\Users\Meisam\Documents\test\" & myID & "_" & fileName) 
        rstAttach.MoveNext() 
       Loop 
      End If 
      rstMain.MoveNext() 
     Loop 
    End If 
    'close the database 
    db.Close() 
End Sub 
İlgili konular