2014-09-16 1 views
6

우리는 Access 데이터베이스에 저장된 데이터를 캐시 데이터베이스로 마이그레이션해야하는 프로젝트 작업을하고 있습니다. Access 데이터베이스에는 데이터 형식이 Attachment 인 열이 있습니다. 일부 튜플에는 여러 개의 첨부 파일이 있습니다. .FileName을 사용하여이 파일의 파일 이름을 가져올 수 있지만 한 파일이 끝나고 다른 파일이 시작될 때를 어떻게 알 수 있습니까? .FileData.Access 데이터베이스의 첨부 파일 필드에서 파일 압축

나는이 데이터를 얻기 위해 다음과 같은 사용하고 있습니다 :

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

답변

8

(이 질문에 대한 내 원래의 대답은 오해의 소지가 있었다 그것은 이후 어도비 리더와 함께 열렸다 PDF 파일을 좋아했다,하지만하지 않았다. 다음은 수정 된 버전입니다.) 불행히도 우리는 OleDb를 사용하여 Access Attachment 필드에서 파일 내용을 직접 검색 할 수 없습니다. Access 데이터베이스 엔진은 파일의 이진 내용에 일부 메타 데이터를 추가하며 OleDb를 통해 .FileData을 검색하면 해당 메타 데이터가 포함됩니다.

설명하기 위해 "Document1.pdf"라는 이름의 문서는 액세스 UI를 사용하여 첨부 파일 필드에 저장됩니다. 이 PDF 파일의 시작은 다음과 같습니다

Original.png

우리가 시도하고 디스크에

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 파일을 추출하려면 다음 코드를 사용하는 경우 다음 결과 파일이 포함됩니다 파일의 시작 부분에있는 메타 데이터 (이 경우 20 바이트)

FromFileData.png

Adobe Reader는 '% PDF-1.4'서명 이전에 파일에 나타날 수있는 "정크"를 무시할 수있을만큼 강력하기 때문에이 파일을 열 수 있습니다. 불행하게도 모든 파일 형식과 응용 프로그램이 파일 시작 부분에서 불필요한 바이트를 허용하지는 않습니다.

Access에서 Attachment 필드에서 파일을 추출의 공식 ™ 방법은, 에이스 DAO Field2 개체의 .SaveToFile 방법을 사용과 같이하는 것입니다

:

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

주 당신이 사용하려고하면 Field2 객체의 .Value은 여전히 ​​바이트 시퀀스의 시작 부분에서 메타 데이터를 가져옵니다. .SaveToFile 프로세스가이를 제거합니다.

가 // 그것은 첨부 파일 필드 내에서 저장된 파일을 검색하는 정보를 조각 같이 걸 렸어요

+0

매우 흥미롭고, 나는 이것을 찾아서 찾을 수 없습니다. – Steve

+1

Gord, 너는 남자 야. 감사. –

+0

어떻게 첨부 파일 열에서 파일 배열을 추출 할 수 있습니까? –

0

// 안녕하세요, 그래서 난 그냥 아이디 공유에게 그것을 생각했다.

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 

    } 
} 

}

// 희망이 사람

0

다음 코드는 마이크로 소프트 액세스 데이터베이스 데이터 테이블의 모든 레코드를 통과하고 레코드에 각 행에 할당을하는 데 도움이됩니다. 는 "Docs"필드에 저장된 모든 첨부 파일을 거칩니다. 그런 다음 해당 파일을 추출하여 디스크에 저장합니다. 이 코드는 위의 "Gord Thompson"이 소개 한 코드의 확장입니다. 내가 한 유일한 이유는 Visual Basic.NET 용 코드를 작성했기 때문입니다.

Imports Microsoft.Office.Interop.Access.Dao 

위의 코드를 사용하여 Dao를 참조하십시오.

'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