2016-08-10 4 views
1

메모리 스트림을 사용하여 이미지를 Access 데이터베이스 테이블에 성공적으로 저장할 수 있습니다. 이미지를 저장하는 열에 "OLEObject"데이터 유형이 있습니다. 테이블을 열면이 이미지 열에 "Long binary data"값이 표시됩니다. 문제는이 데이터베이스에서 보고서를 만들 때 이미지를 볼 수 없다는 것입니다."비트 맵 이미지"로 데이터베이스에 이미지 저장

MSPaint 및 Ctrl + A에서 이미지 파일을 연 다음 Ctrl + C를 눌러 모든 그래픽을 복사 한 다음 Access 데이터베이스 열에 붙여 넣었을 때 성공적으로 붙여 넣기되었습니다. Now는 value를 "비트 맵 이미지"로 표시합니다. 이미지 열 값을 두 번 클릭하면 MSPaint에서 이미지가 열리고 보고서에서도 작동합니다. 이미지를 "비트 맵 이미지"유형으로 직접 저장하는 방법을 알고 싶습니다. 나는 VB 닷넷 프레임 워크 4.0,

Image added thru VB .Net

Manually pasted from MSPaint

+0

거기에 어떤 보고서를 사용하고 있습니까? 결정? –

+0

아니요, MS Access 보고서 마법사를 사용하여 보고서를 만드는 중 ... – DK2014

+0

정말 오랫동안 액세스하지 못했습니다 ... 문제가 일부 관련 될 수 있다고 생각합니다. 스트림 중에 복사하지 않는 이미지의 헤더 . 스트림을 사용하는 대신 FILE.READALLBYTES()를 사용하여 데이터를 거기에 두려고 했습니까? 내 말은이 문제가 스트림 자체와 관련이있을 수 있다는 것입니다. 스트림을 닫기 전에 스트림을 플러시하고 있습니까? –

답변

2

는 MS 액세스 OLE 필드 구조 2012 내가 찾을 수있는 문헌에 설명하지 않은 비주얼 스튜디오를 사용하고 있습니다.

아래 클래스 AccessOLEBitmapConverter 메소드 GetBitmapStreamSystem.Drawing.ImageConverter Class 대한 소스 코드 내의 참조 문서 (How To Retrieve Bitmap from Access and Display It in Web Page)에 기초 포렌식 해킹의 결과이다.

해킹 비트를 기반으로 필드가 두 개의 구조, 즉 1) 헤더 구조와 2) EmbeddedObject 구조로 이루어져 있다는 것을 발견했습니다. 더 많은 해킹을 수행 한 결과 EmbeddedObject 구조의 Presentation 필드는 1232 시퀀스로 대체 될 수 있으며 헤더 구조의 name 필드는 6 바이트 시퀀스 ({33, 0, 255 , 255, 255, 255}). 자세한 내용은 코드를 참조하십시오.

Public Class AccessOLEBitmapConverter 
    Const nullByte As Byte = 0 
    Const OLEVersion As Int32 = 1281 
    Private Shared arrayInvalid As String = "Source array is invalid" 

    Public Shared Function ToByteArray(sourceBitmap As Bitmap) As Byte() 

     Dim ret As Byte() = Nothing 
     Using ms As New IO.MemoryStream(5000) 
      EmbeddedObjectHeader.Write(ms) 
      EmbeddedObject.Write(ms, sourceBitmap) 
      ret = ms.ToArray() 
     End Using 

     Return ret 
    End Function 

    Public Shared Function ToBitmap(bytes As Byte()) As Bitmap 
     Dim ret As Bitmap = Nothing 

     If bytes Is Nothing Then Throw New ArgumentNullException("Argument ""bytes"" cannot be null") 
     If bytes.Length < 14 Then Throw New ArgumentOutOfRangeException(arrayInvalid) 
     Using ms As New IO.MemoryStream(bytes, False), 
       br As New IO.BinaryReader(ms, System.Text.Encoding.ASCII) 
      Dim signature As Int16 = br.ReadInt16() 
      If signature <> &H1C15 Then Throw New ArgumentOutOfRangeException(arrayInvalid) 

      Dim headersize As Int16 = br.ReadInt16() ' 47 
      If bytes.Length < headersize Then Throw New ArgumentOutOfRangeException(arrayInvalid) 

      ms.Position = headersize 

      ' Start ObjectHeader 
      If ms.Position = bytes.Length Then Throw New ArgumentOutOfRangeException(arrayInvalid) 
      Dim OLEVersion As UInt32 = br.ReadUInt32 
      If ms.Position = bytes.Length Then Throw New ArgumentOutOfRangeException(arrayInvalid) 
      Dim FormatID As UInt32 = br.ReadUInt32 

      If ms.Position = bytes.Length Then Throw New ArgumentOutOfRangeException(arrayInvalid) 
      Dim ClassName As String = LengthPrefixedAnsiString.Read(br) 

      If ms.Position = bytes.Length Then Throw New ArgumentOutOfRangeException(arrayInvalid) 
      Dim TopicName As String = LengthPrefixedAnsiString.Read(br) 

      If ms.Position = bytes.Length Then Throw New ArgumentOutOfRangeException(arrayInvalid) 
      Dim ItemName As String = LengthPrefixedAnsiString.Read(br) 
      ' End ObjectHeader 

      If ms.Position = bytes.Length Then Throw New ArgumentOutOfRangeException(arrayInvalid) 
      Dim NativeDataSize As Int32 = br.ReadInt32 

      If (ms.Position + NativeDataSize) > bytes.Length Then Throw New ArgumentOutOfRangeException(arrayInvalid) 
      Dim NativeData As Byte() = br.ReadBytes(NativeDataSize) 

      Dim msImage As New IO.MemoryStream(NativeData) 

      ret = CType(Image.FromStream(msImage), Bitmap) 
     End Using 


     Return ret 
    End Function 

    Private Class EmbeddedObjectHeader 
     ' ref: How To Retrieve Bitmap from Access and Display It in Web Page 
     ' https://support.microsoft.com/en-us/kb/175261 

     Friend Shared Sub Write(ms As System.IO.MemoryStream) 
      Const signature As Int16 = &H1C15 
      Const headersize As Int16 = 47S 
      Const objectType As Int16 = 2S 
      Const nameLen As Int16 = 0S 
      Const classLen As Int16 = 13S 
      Const nameOffset As Int16 = 14S 
      Const classOffset As Int16 = 20S 
      Const classType As String = "Bitmap Image" 
      Const hdrClassName As String = "Paint.Picture" 

      Using bw As New IO.BinaryWriter(ms, System.Text.Encoding.ASCII, True) 
       With bw 
        .Write(signature) 
        .Write(headersize) 
        .Write(objectType) 
        .Write(nameLen) 
        .Write(classLen) 
        .Write(nameOffset) 
        .Write(classOffset) 

        ' Even though this offset is declared as being for the 'name' field and 
        ' the 'name' field always has a zero length, these six bytes must be present 
        ' to allow the resultant byte array to be identified as a BitMap Image by Access 
        ms.Position = nameOffset 
        .Write(New Byte() {33, 0, 255, 255, 255, 255}) 

        ms.Position = classOffset 
        .Write(classType.ToCharArray()) 
        .Write(nullByte) 
        .Write(hdrClassName.ToCharArray()) 
        .Write(nullByte) 
       End With 
      End Using 
     End Sub 
    End Class ' EmbeddedObjectHeader 

    Private Class EmbeddedObject 
     ' ref: https://msdn.microsoft.com/en-us/library/dd942053.aspx 
     'Header (variable): This MUST be an ObjectHeader (section 2.2.4). 
     ' The FormatID field of the Header MUST be set to 0x00000002. 

     'NativeDataSize (4 bytes): This MUST be set to the size of the NativeData field, in bytes. 

     'NativeData (variable): This must be an array of bytes that contains the native data. 

     'Presentation (variable): This MUST be a MetaFilePresentationObject (section 2.2.2.1), 
     ' a BitmapPresentationObject (section 2.2.2.2), a DIBPresentationObject (section 2.2.2.3), 
     ' a StandardClipboardFormatPresentationObject (section 2.2.3.2), or 
     ' a RegisteredClipboardFormatPresentationObject (section 2.2.3.3). 

     Friend Shared Sub Write(ms As System.IO.Stream, sourceBitmap As Bitmap) 

      Using bw As New IO.BinaryWriter(ms, System.Text.Encoding.ASCII, True) 
       With bw 
        ObjectHeader.Write(ms) 

        ' Determine and write the NativeDataSize and NativeData fields 
        Using imgStream As New IO.MemoryStream(20000) 
         sourceBitmap.Save(imgStream, System.Drawing.Imaging.ImageFormat.Bmp) 
         Dim NativeDataSize As Int32 = CInt(imgStream.Length) 
         .Write(NativeDataSize)  
         .Write(imgStream.ToArray) 
        End Using 

        ' At this point the 'Presentation' variable should be written. 
        ' However, Bitmap files copied from Windows Explorer and pasted into 
        ' the MS Access OLE field have only 12 bytes written and this allows for 
        ' a much smaller size to be stored as the 'Presentation' variable appears to 
        ' duplicate the NativeData field. Adding the Bitmap via the 'Insert Object' 
        ' dialog creates a storage nearly twice that of this method. 

        ' The first 4 bytes correspond to the integer value for OLEVersion. 
        .Write(OLEVersion) 
        ' The next 4 bytes are always zero. 
        .Write(0I) 
        ' The next byte (position 8) appears variable and its value does not appear 
        ' to impact using the 'BitMap Image' in Access. So write a zero. 
        .Write(nullByte) 
        ' The final three bytes appear to be constant {173, 5, 254} 
        .Write(New Byte() {173, 5, 254}) 
        .Flush() 
       End With 
      End Using 
     End Sub 

    End Class 'EmbeddedObject 

    Private Class ObjectHeader 
     Friend Shared Sub Write(ms As System.IO.Stream) 
      Const FormatID As Int32 = 2 
      Const ClassName As String = "PBrush" 
      Const TopicName As String = "" 
      Const ItemName As String = "" 

      Using bw As New IO.BinaryWriter(ms, System.Text.Encoding.ASCII, True) 
       With bw 
        .Write(OLEVersion) 
        .Write(FormatID) 
        LengthPrefixedAnsiString.Write(bw, ClassName) 
        LengthPrefixedAnsiString.Write(bw, TopicName) 
        LengthPrefixedAnsiString.Write(bw, ItemName) 
       End With 
      End Using 
     End Sub 

    End Class ' ObjectHeader 

    Private Class LengthPrefixedAnsiString 
     ' ref : https://msdn.microsoft.com/en-us/library/dd942554.aspx 
     ' This structure specifies a null-terminated American National Standards Institute (ANSI) character set string. 
     ' Length (4 bytes): This MUST be set to the number of ANSI characters in the String field, 
     ' including the terminating null character. Length MUST be set to 0x00000000 to indicate an empty string. 
     ' String (variable): This MUST be a null-terminated ANSI string. 

     Const nullChar As Byte = 0 

     Friend Shared Function Read(br As IO.BinaryReader) As String 
      Dim ret As String = String.Empty 
      Dim length As Int32 = br.ReadInt32 
      If length > 0 Then 
       Dim chars As Char() = br.ReadChars(length) 
       ret = New String(chars) 
      End If 
      Return ret 
     End Function 

     Friend Shared Sub Write(bw As IO.BinaryWriter, val As String) 
      If val.Length = 0 Then 
       bw.Write(0I) 
      Else 
       bw.Write(val.Length + 1) 
       bw.Write(val.ToCharArray) 
       bw.Write(nullChar) 
      End If 
     End Sub 
    End Class 'LengthPrefixedAnsiString 
End Class 

사용 예제 :

' To obtain a Byte() to store a Bitmap to MS Access 
Dim bm As Bitmap = CType(Image.FromFile("SomeBitmapFile.bmp"), Bitmap) 
Dim bytesToStoreInAccess As Byte() = AccessOLEBitmapConverter.ToByteArray(bm) 

' To retrieve a Bitmap from an Access Bitmap Image field. 
Dim bytesFromBitmapImageField As Byte() ' set this equal to the field data 
Dim bmRetrieved As Bitmap = AccessOLEBitmapConverter.ToBitmap(bytesFromBitmapImageField) 

이 코드는 성공적으로 될 수있는 MS 액세스 Bitmap Image를 산출하기 위해 액세스 OLE 필드에 비트 맵을 저장하는 윈도우 10 컴퓨터에 MS 액세스 2007을 사용하여 테스트되었습니다 액세스 보고서에 표시됩니다.

+0

TnTinMn이 매우 상세한 코드와 노력에 많은 감사를드립니다. 내 응용 프로그램에 클래스를 추가했지만이 줄에 오류가 발생했습니다. '새 IO.BinaryWriter (ms, System.Text.Encoding.ASCII, True)로 bw 사용'오류 : 오류 액세스 할 수 없기 때문에 오버로드 확인에 실패했습니다. '는이 인수를 허용합니다. \t이 문제에 대해 조언 해 주실 수 있습니까? 여기에 아무것도 가져와야합니까? – DK2014

+0

@ DK2014 그냥 새 문장을 제거 : bw를 Io.Binary로 사용 .... –

+0

@ David BS, 나는 이미 시도했지만 코드가 더 이상 나지 않습니다. bw를 새로운 IO.BinaryWriter (MS_Write, System.Text.Encoding.ASCII, True)로 변경하여 bw를 새 IO로 변경했습니다.BinaryWriter (MS_Write, System.Text.Encoding.ASCII) 적어도 오류를 없애고 변환 중에 사용하면 여러 단계의 메모리 스트림과 관련된 오류가 있습니다. 나는 다른 접근법을 시도해 왔지만 지금까지 아직 운이 좋지는 않습니다. – DK2014

관련 문제