Tôi có tham khảo cách mà người ta tạo ra file hình bằng những con số liên tục trong một danh sách (như file bên dưới). Vậy các anh (chị) cho tôi hỏi làm cách nào mà từ một hình ảnh tôi có thể tạo được một danh sách các số như ô A1 trong file dưới. Mục đích là để từ một ảnh bất kỳ tôi có thể đưa nó sang một danh sách số như trong file để sang máy khác tôi sẽ dùng code của file dưới để chuyển danh sách số này sang lại file hình trên máy đó (tránh trường hợp khi người dùng copy sẽ làm mất file hình kèm theo).
Hình trên là table của SQL Server. Dùng ADODB.Stream để đọc file ảnh rồi mới dùng câu lệnh T-SQL lưu trực tiếp vào field của Table.
Còn Access thì không cần. Chỉ cần khai báo field là OLEObject và Access sẽ hỗ trợ nút lệnh "Insert Object" chèn thẳng vào field.
À. Vụ hiển thị ảnh trong Access Form thì vẫn phải đi đường vòng bác à. Tức là vẫn phải chuyển từ Binary sang file ảnh và lưu vào folder tạm, sau đó lấy đường dẫn đó gán cho Image control trên Form Access.
Công nhận bác hay thật nhìn cái hình mã binary suy ra JPG, PNG luôn.
Hi hi, ruột là JPEG mà bác Ông Kẹ ép nó lưu xuống với đuôi png hen MaNV = 2 là JPEG, MaNV = 1 là bitmap, size = 0x00035A06 (219 654) bytes.
JPEG file thì bytes nhận dạng là = FF D8 FF E0, PNG file byte nhận dạng là 89 50 4E 47....
Hồi xưa mình làm bên multimedia, xử lý ảnh, video... mấy cái này làm nát luôn, giờ nhìn thấy là ngán mà. Nhất là ông BMP, sợ nó luôn
Lãnh vực forensics bây giờ phải nắm file format rất nhiều. Không biết gì thì cứ hỏi ông Google, ê cho tui xxx file format
Hi hi, ruột là JPEG mà bác Ông Kẹ ép nó lưu xuống với đuôi png hen MaNV = 2 là JPEG, MaNV = 1 là bitmap, size = 0x00035A06 (219 654) bytes.
JPEG file thì bytes nhận dạng là = FF D8 FF E0, PNG file byte nhận dạng là 89 50 4E 47....
Hồi xưa mình làm bên multimedia, xử lý ảnh, video... mấy cái này làm nát luôn, giờ nhìn thấy là ngán mà. Nhất là ông BMP, sợ nó luôn
Lãnh vực forensics bây giờ phải nắm file format rất nhiều. Không biết gì thì cứ hỏi ông Google, ê cho tui xxx file format
Haha...đúng là làm biếng chút là bị bắt lỗi liền. Đúng ra là trong table còn có 1 field nữa tôi lưu [FileExtension] sau đó khi convert sẽ lấy đuôi đó ghép vô chứ không có ép như code demo.
Không cần đâu field đó đâu bác Ông kẹ, cứ lưu đại xuống với đuôi gì cũng đươc, mọi chuyện file content là gì thì đã có Office "no". Nó sẽ tự detect được file format là gì và phải dùng parser gì
Không cần đâu field đó đâu bác Ông kẹ, cứ lưu đại xuống với đuôi gì cũng đươc, mọi chuyện file content là gì thì đã có Office "no". Nó sẽ tự detect được file format là gì và phải dùng parser gì
À, thế thì phải thêm field đó rồi. 2 file type khác. Sorry ông kẹ nhen
File Office cũ thì luôn là D0 CF 11 E0 ở đầu file. Coder MS code cố tình chọn 4 byte đó, đó là DocFile đó các bạn, nhìn thấy không
Tui cũng hay dùng 0x0BADC0DE (bad code) đễ đánh dấu memory đít và đầu để detect bị dập memory.
File pdf thì 0x25 50 44 46 (%PDF) ở đầu. File Office mới bây giờ thì Zip format, 0x50 4B (PK) đầu tiên.
Các ct forensics, khôi phục file trên đĩa luôn luôn nhận dạng loại file dựa vào các bytes nhận dạng này để rút trích ngược lại file.
Cứ download HxD và mở ra sẽ thấy, coi như biết thêm cho vui
Hex editor miễn phí và mạnh thì chỉ có HxD là số 1, được viết = Delphi nhé, các bác download tại chính gốc đây:
Còn trình zip thì bà con đừng dùng WinRAR, lỗi bảo mật nhiều lắm, bị hacker khai thác nhiều. Cứ 7zip free mà dùng, internal viewer của nó support rất nhiều file format độc như PE, msi, các trình setup...
Vậy là Ông Kẹ không cần fied FileExtension nữa nhen, cứ đọc 4 byte đầu của stream lên rồi detect format.
Còn có việc phải nhờ Ong Kẹ test đấy
Mã:
#If USE_FOR_ACCESS Then ' Dành riêng cho Ông Ke
Public Property Get aeArrayFromPicture(ByVal objPic As StdPicture, ByVal pic As aePicFileType) As Byte()
aeArrayFromPicture = ArrayFromPicture(objPic, pic)
End Property
Public Property Get aeResampleGDIP(ByVal Image As StdPicture, ByVal Width As Long, ByVal Height As Long) As StdPicture
Set aeResampleGDIP = ResampleGDIP(Image, Width, Height)
End Property
Public Property Get aeLoadPictureGDIP(ByVal sFileName As String) As StdPicture
Set aeLoadPictureGDIP = LoadPictureGDIP(sFileName)
End Property
Public Property Get aeAttachmentToPicture(ByVal strTable As String, ByVal strAttachmentField As String, ByVal strImage As String) As StdPicture
Set aeAttachmentToPicture = AttachmentToPicture(strTable, strAttachmentField, strImage)
End Property
Public Property Get aeOLEFieldToPicture(ByVal strTable As String, ByVal strNameField As String, ByVal strName As String, ByVal strOLEField As String) As StdPicture
Set aeOLEFieldToPicture = OLEFieldToPicture(strTable, strNameField, strName, strOLEField)
End Property
' Create a picture object from an OLE Field (BLOB, long binary)
' strTable: Table containing OLE field with picture contents
' strNameField: Name field to identify record
' strName: Unique name of the picture in Name field
' strOLEField: Name of OLE field in table
' ? OLEFieldToPicture("tblOLE","ImageName","cloudy","Blob").Width
Private Function OLEFieldToPicture(ByVal strTable As String, _
ByVal strNameField As String, _
ByVal strName As String, _
ByVal strOLEField As String) As StdPicture
Dim rst As ado.Recordset
Set rst = CurrentDb.OpenRecordset("SELECT " & strOLEField & " FROM " & strTable & " WHERE " & strNameField & "='" & strName & "'", dbOpenDynaset)
If Not rst.EOF Then
Set OLEFieldToPicture = ArrayToPicture(rst(strOLEField).Value)
End If
rst.Close
Set rst = Nothing
End Function
' Create a picture object from an Access 2007 attachment
' strTable: Table containing picture file attachments
' strAttachmentField: Name of the attachment column in the table
' strImage: Name of the image to search in the attachment records
' ? AttachmentToPicture("ribbonimages","imageblob","cloudy.png").Width
Private Function AttachmentToPicture(ByVal strTable As String, ByVal strAttachmentField As String, ByVal strImage As String) As StdPicture
Dim strSQL As String
Dim bin() As Byte
Dim nOffset As Long
Dim nSize As Long
strSQL = "SELECT " & strTable & "." & strAttachmentField & ".FileData AS data " & _
"FROM " & strTable & _
" WHERE " & strTable & "." & strAttachmentField & ".FileName='" & strImage & "'"
On Error Resume Next
bin = DBEngine(0)(0).OpenRecordset(strSQL, dbOpenSnapshot)(0)
If Err.Number = 0 Then
Dim bin2() As Byte
nOffset = bin(0) ' First byte of Field2.FileData identifies offset to the file data block
nSize = UBound(bin)
ReDim bin2(nSize - nOffset)
CopyMemory bin2(0), bin(nOffset), nSize - nOffset ' Copy file into new byte array starting at nOffset
Set AttachmentToPicture = ArrayToPicture(bin2)
Erase bin2
Erase bin
End If
End Function
#End If ' USE_FOR_ACCESS
Vậy là Ông Kẹ không cần fied FileExtension nữa nhen, cứ đọc 4 byte đầu của stream lên rồi detect format.
Còn có việc phải nhờ Ong Kẹ test đấy
Mã:
#If USE_FOR_ACCESS Then ' Dành riêng cho Ông Ke
Public Property Get aeArrayFromPicture(ByVal objPic As StdPicture, ByVal pic As aePicFileType) As Byte()
aeArrayFromPicture = ArrayFromPicture(objPic, pic)
End Property
Public Property Get aeResampleGDIP(ByVal Image As StdPicture, ByVal Width As Long, ByVal Height As Long) As StdPicture
Set aeResampleGDIP = ResampleGDIP(Image, Width, Height)
End Property
Public Property Get aeLoadPictureGDIP(ByVal sFileName As String) As StdPicture
Set aeLoadPictureGDIP = LoadPictureGDIP(sFileName)
End Property
Public Property Get aeAttachmentToPicture(ByVal strTable As String, ByVal strAttachmentField As String, ByVal strImage As String) As StdPicture
Set aeAttachmentToPicture = AttachmentToPicture(strTable, strAttachmentField, strImage)
End Property
Public Property Get aeOLEFieldToPicture(ByVal strTable As String, ByVal strNameField As String, ByVal strName As String, ByVal strOLEField As String) As StdPicture
Set aeOLEFieldToPicture = OLEFieldToPicture(strTable, strNameField, strName, strOLEField)
End Property
...
#End If ' USE_FOR_ACCESS
Hì hì, thôi để mình up đầy đủ lên đây cho Ông Kẹ với các bạn làm tiếp luôn, chứ nói thật tình là mình ngán mấy cái vụ xử lý ảnh với dùng API với VBA tới tận cổ luôn rồi. Không muốn, không có hứng làm chút nào. Làm 1 mình cũng ngán nữa.
Dùng API trên C# mình đã chữi um cái vụ khai báo với UnSafe của nó rồi huống chi VBA.
Mình chỉ dùng API với VC và Delphi thôi, cứ Pointer mà phang thôi, quen rồi. Hỏi bác Tuân thì biết.
Máy mình lại là Office 2007 nữa (VBE 6) nên làm xong phải port, test với VBE7 rồi Office64 nữa, nên "nười" quá bỏ luôn. Mà bỏ thì cũng uổng công đã tìm, port, gọt giũa, nên thôi up lên đây.
Bà con làm tiếp nhen, cơ bản trong file chúng ta đã có các hàm từ memory byte buffer to GDIPlus image và ngược lại. Có StdPicture rồi thì cứ get hay assign vào các đối tượng nào của Office support property StdPicture (IPicture).
Có byte buffer đó rồi các bác muốn làm, muốn chế sang Hex/Base64/xxx và ngược lại gì thì tùy các bác thêm mắm, thêm muối và nhu cầu sử dụng.
À quên, cứ image file format nào mà Excel mở được thì thư viện này mở được hết nhé, còn tại sao thì mình sẽ nói sau. Hồi sau sẽ rõ
Tốc độ thì các bác khỏi phải "no". API nó làm ở DLL viết = Visual C++ hết chứ VBA có làm cái gì đâu.
Bài đã được tự động gộp:
Tốt nhất là bác nào nắm được gọi API với VBA và đang có máy Win64 + Office64 ngồi port tiếp là tốt nhất. Win64 + Office64 mà chạy đúng thì Win32 + Office32 chắc chắn sẽ đúng, mà ngược lại thì không chắc nhen.
Có gì thét mét đừng ngại post lên trao đổi nhé các bác, cho nó xong vấn đề này cho rồi.
Mình thì bữa giờ kẹt với bác Tuân quá và không muốn làm cái này nữa.
Chổ USE_FOR_ACCESS mình phải bật False và chưa test, debug đó nhen ông kẹ, vì bật True thì khi compile VBA của Excel nó nhảy vào chửi mình, kekeke
Ông kẹ cứ bật True lên hay xóa nó đi trong môi trường Access của bạn nhé. Mình test trên Excel 2007 32bit là OK hết rồi đó.
Chổ CopyMemory bin2(0), bin(nOffset), nSize - nOffset, bên mình khai báo là ByVal xxx as Long/LongPtr nhé, nên ông kẹ chịu khó sữa thành sau nhé.
Không biết các bác @giaiphap, @ongke0711 port tới đâu rồi nhỉ, có làm tiếp hay không nhỉ, có kẹt, vướng mắc gì không ?
Về vụ File IO binary mode thì mình, như đã nói bên topic TextStream rồi, mình đã bo xì, nghĩ chơi VBA IO Funcs và TextStream, đã tìm ra giải pháp thay thế, dùng API của shlwapi.dll (Shell Lighweight API Library) của MS. Nó là các hàm IStream_xxx. shlwapi.dll này nói thật mình rất chi là "ưng cái bụng", hầu như cái gì cũng có cho những nhu cầu thông thường của mọi người. Và nó cũng được coder MS dùng rất nhiều trong các team, soft, component của họ. Ví dụ như ông Excel bà con đang chạy, nó cũng dùng dll này, ở dạng Delay Load (load trễ: kiểu trung gian giữa static link và dynamic call GetProcAddress). Khi trong code VBA bà con gọi nó thì sure 200% nó đã được load lên memory process của Excel.exe rồi. Nên VBDllFuncCall sẽ đở vất vã
Bên trong IStream_xxx, nó thao tác với API cấp thấp nhất, ntdll.dll API, nên cực nhanh và mạnh.
Các Object khác, phía trên của MS như ADO Stream, XML Stream... đều phải dùng, call tới các API này bên trong ruột.
Nhưng có 1 cái kẹt nhỏ xíu là nó viết ra với mục đích là dùng, để call cho/từ C++. Nó trả về và nhận vào pointer to IStream interface. Mà IStream interface thì VB/VBA không support, declare.
Nhưng không sao, 1 chút xíu tip, workaround nho nhỏ là mấy em nó phải ngoan ngoãn nghe lời, làm đúng theo anh VBA gọi à
Code VBA dùng, call IStream_Read/IStream_Write... không có trên Google đâu, chưa ai làm cả, bà con khỏi tìm mất công.
Bài đã được tự động gộp:
Về vụ Hex, Base64 thì các Dll của Windows không có thằng nào export ra hàm API nào để làm cả. Nên sau đó mình đã tìm trong các COM Object và đã tìm ra. Đó là capicom.dll. Dll của MS, luôn có với mọi version của Windows nhé, nên bà con an tâm. capicom = Crypto API COM Library. Phục vụ các tác vụ về mã hóa, crypto... tùm lum tùm la của MS. Trong CAPICOM có class Utilities, có các method làm các vụ Hex/Binary/Base64 này. Mình đã xem trong mã của nó và phải công nhận, nó code cực nhanh (MS coder siêu quá).
Bà con cứ add nó vào và tham khảo nhé. Trong References, Browse, nó nằm đầu tiên ở vần C đó. Cứ code các hàm nho nhỏ test các method của nó. Thích early bind thì add capicom.dll vào References, new CAPICOM.Utilities. Còn không muốn add Reference thì CreateObject("CAPICOM.Utilities").
Bài đã được tự động gộp:
Interface cho các bác code Delphi nó "như thế lày" đây
Tiếp tục chủ đề các bạn ơi, chưa xong mà.
- Vụ byte array sang Image StdPicture và ngược lại chúng ta đã coi như xong. Chỉ còn chờ các bạn port và test thôi.
- Vụ từ byte array sang hex string và ngược lại thì chúng ta cũng đã xong, bên topic Undocument VBA. Tốc độ tạm tạm "ược"
Giờ chỉ còn vụ IO, vì chúng ta không dùng TextStream và Open statement của VBA được.
Tui code mình tui dùng thì không nói gì rồi, nhưng muốn share cho các bạn dùng sau này luôn, nên mới hỏi các bạn cần các tính năng nào của hàm.
Ví dụ tôi giờ chỉ thấy cần dùng 2 hàm tôi đặt tên là
FileBinary_ReadAll(strPath as string, arrData() as Byte) as Boolean: Read tất cả content của 1 file chỉ ra trong strPath vào arrData. 1 lần call duy nhất.
FileBinary_WriteAll(strPath as String, arrData() as Byte) as Boolean: Write, ghi đè toàn bộ nội dung file cũ của file chỉ bởi strPath bằng data trong arrData. 1 lần call duy nhất.
Vì strPath là string VBA Unicode giữ nguyên bản truyền xuống cho các API IStream_ nên các bạn không lo vụ file có dấu.
Và IStream_ support file cực bự, 2^64 byte trên cả Win32 và Win64, các bạn có cần đến không, hay chỉ cần trong phạm vi biến Long dương của VBA, 2GB thôi ?
Và các bạn cần hàm nào nữa để dùng sau này thì cứ liệt kê ra, mình viết 1 lần vào modUtils.pas cho sau này các bạn mang đi độc lập xài luôn, không dính dáng gì tới clsGDIPlug và modAPITypes ? Cứ môi trường VBA của Office xài được !
Hình 1 là dùng kiểu dữ liệu "Attachment" có sẳn từ Access 2007 trở lên, đem qua các máy xài Office phiên bản thấp hơn là không chạy được.
Hình 2 là dùng kiểu dữ liệu OLE Object, kiểu dữ liệu này thì Access 2003 cũng có nên tính tương thích cao hơn.
Muốn thao tác nhanh thì dùng Attachment, tải file ảnh lên để lưu nhanh. Còn cách 2 thì dùng phương thức GETCHUNK/ APPENDCHUNK của thư viện DAO hoặc ADO để convert file ảnh thành dạng BInary rồi lưu xuống Field OLE Object.
2 cách trên đều lưu file ảnh vào hệ thống Table của Access sẽ làm file tăng dung lượng nhanh. Tại sao bạn không dùng đường dẫn file ảnh để nhẹ nhàng hơn. Tôi thường dùng cách 2 (lưu xuống Table) cho các file ảnh Logo, Icon nút lệnh dung lượng vài chục kB để khi truy xuất sẽ nhanh hơn thôi.