Shtimi dhe Nxjerrja e Objekteve BLOB nga Databaza
"Si mund te ruaj nje foto (imazh) ose nje artikull te madh ne databaze dhe si ta mundesoj nxjerrjen (leximin) e saj nga databaza ne programin tim?" eshte nje pyetje qe behet shpesh edhe nga programues me nje eksperience jo fare te shkurter, per arsye se jo shpesh hasen specifikime te tilla nga pjesa me e madhe e perdorueseve te ardhshem te sistemit.
Fare thjesht, mendimi i pare qe te vjen ndermend, eshte te ruash vetem adresen e objektit (fotos ose artikullit) ne nje kolone te tabeles perkatese. (Me 'adrese' nenkuptojme vendndodhjen fizike, duke perfshire edhe shtegun ne nje kompjuter). Megjithate, ky trajtim ka pasojat e veta. Se pari, nese vendndodhja e objektit ndryshon (pra, objekti fshihet, levizet ne nje vend tjeter), atehere adresa e ruajtur ne databaze nuk mund te azhurnohet automatikisht. Kjo do te thote se, per nje numer te madh rekordesh, adresat perkatese te objekteve te tyre humbasin 'koherencen' me vete objektet, duke sjelle nje humbje te kontrollit te programit (pra, te perdoruesit) mbi keto objekte. Se dyti, nese programi mundeson ruajtjen e objekteve ne te njejtin server ku gjendet databaza, atehere per cdo objekt te ri duhet mundesuar edhe kopjimi (ose levizja) automatike ne server. Kjo mund te kete pasoja te paparashikueshme.
Zgjidhja tjeter ka te beje me transferimin e objektit (permbajtjen e tij) nga vendndodhja fizike e dhene ne fushen e tabeles perkatese ne databaze. Ne kete artikull do te paraqes disa koncepte kryesore, ndonje leksion nga eksperienca ime, si dhe kodin (VB6/ADO2.6) per procedurat per shtimin/nxjerrjen e objekteve "te medha" nga databaza. (Kodi eshte bazuar plotesisht ne artikullin e MS Knowledge Base http://support.microsoft.com/kb/194975, por me modifikime te pershtatura. Te njejtat procedura per VB.NET/ADO.NET mund t'i gjeni ne http://support.microsoft.com/kb/317034/EN-US/.) Per thjeshtesi, do t'i referohemi emrave te tipeve te perdorura nga SQL 2000/.NET.
C'eshte tipi BLOB?
BLOB eshte akronimi i Binary Large OBjects, perkthyer Objekte te Medha Binare, i cili identifikon imazhet dhe sasite e medha te karaktereve te nderlidhura. Ne gjuhen e databazes, tipet e kolonave te nje tabele te identifikuara si IMAGE, TEXT dhe NTEXT nenkuptojne objekte BLOB. (Fusha NTEXT perdoret per karaktere UNICODE. Diskutimi per UNICODE bie jashte sferes se ketij artikulli; per me teper mund te lexoni ne shqip kete artikull.)
Active Data Objects (ADO)
ADO eshte objekti qe mundeson lidhjen me databazen (fale objektit Connection), nxjerrjen e nje bashkesie rekordesh (fale objektit Command), manipulim i ketyre rekordeve (fale objektit RecordSet). Me pak fjale, ADO mundeson te katerta veprimet e mundshme mbi nje databaze: SELECT (nxjerrja e rekordeve), INSERT (shtimi i rekordeve), DELETE (fshirja e rekordeve), dhe UPDATE (azhurnimi i rekordeve).
Nga kendveshtrimi hierarkik, objekti RecordSet permban nje koleksion objektesh te tipit Field, pra, te gjitha fushat e rekordeve te zgjedhura. Me ane te ketij objekti, ne mund te lexojme, nder te tjera, emrin dhe vleren e cdo fushe te cdo rekordi te nxjerre nga databaza. Tre nga metodat kryesore te objektit Field qe nevojiten per qellimin e ketij artikulli jane AppendChunk(), GetChunk(), dhe Update(). (Perkthyer ne menyre literale, fjala 'chunk' do te thote 'llokėm' (si ne 'llokem mishi') ose 'copė'.) Metodat AppendChunk() dhe GetChunk() marrin si parameter gjatesine (ne bajte) te "llokmes" se te dhenave qe do te shtohen/nxirren ne fushen e dhene. Keto procedura kerkojne madhesine e fushes, e cila mund te merret fale vecorise ActualSize te objektit Field. Ndersa, metoda e paparametrizuar Update azhurnon te dhenat e nje fushe te caktuar. Sic vetekuptohet, dy metodat e para do te perdoren per shtimin/nxjerrjen e objekteve BLOB nga databaza.
Shtimi i te Dhenave BLOB
Para se nje objekt BLOB te shtohet ne databaze, duhet qe objekti te ruhet ne forme fizike ne nje disk. Pra, supozohet qe te dhenat te ekzistojne fizikisht. Pershembull, nje foto e sapo-skanuar ka emrin borix.jpg dhe eshte ruajtur ne direktorine C:\. (Ia vlen te permendet fakti se ekstensioni i skedarit te tipit imazh nuk ka rendesi per procedurat e leximit te bajteve ne te. Pra, 'borix.jpg' mundet fare mire te jepet si 'borix.tmp' ose vetem 'borix'. Ne cdo rast, bajtet ekzistojne te mbeshtjella ne objekt, per sa kohe qe nuk ka pesuar ndonje nderhyrje manuale.)
Meqenese cdo informacion ne kompjuter ruhet ne forme dysore (binare), metodat AppendChunk dhe GetChunk kerkojne si parameter nje bllok te caktuar bajtesh (ose 8 bitesh). Artikulli nga Knowledge Base rekomandon nje madhesi prej dy ne fuqi te katermbedhjete (2^14) ose 16384 bajtesh. (Na nevojitet njesia bajt, sepse imazhi lexohet me kete njesi.)
Kodi i meposhtem paraqet nje procedure File2BLOB(), me keto parametra:
○ FName - shtegu dhe emri i skedarit (psh.: C:\borix.tmp)
○ fld - reference e objektit Field, pra fusha ku do te shtohet BLOB-i.
○ Threshold - kufiri i madhesise se skedarit. Nese skedari ka nje madhesi me te vogel se sa kjo vlere konstante, atehere te dhenat BLOB mund te shtohen direkt ne fushe, pa patur nevoje te therritet metoda AppendChunk.
Kodi:
' Deklarime te pergjithshme:
Global Const BLOCK_SIZE = 16384 ' 2^14 bajte
Kodi:
Public Sub File2Blob(ByVal FName As String, fld As ADODB.Field, _
Optional Threshold As Long = 1048576)
' Supozime: Skedari ekziston. Me kete procedure do te azhurnohet (UPDATE) fusha.
' Madhesia eshte jo me e madhe se 2 Gb.
On Error GoTo err_log
Dim f As Long, Data() As Byte, FileSize As Long
f = FreeFile
Open FName For Binary As #f
FileSize = LOF(f) ' length of file - percakton madhesine
Select Case fld.Type
Case adLongVarBinary ' nese eshte imazh (foto)
If FileSize > Threshold Then
' Skedari eshte me i madh se vlera kufizuese Threshold,
' prandaj duhet thirrur metoda AppendChunk, e cila gjendet
' ne proceduren Read2Binary():
Call Read2Binary (f, fld, FileSize)
Else
' madhesia e skedarit eshte e pranueshme, prandaj
' te dhenat BLOB te shtohen drejtperdrejt ne fushen fld
Data = InputB(FileSize, f) ' InputB nenkupton Input Bajte
fld.value = Data ' Data eshte nje vektor bajtesh (shih deklarimin)
End If
Case adLongVarChar, adLongVarWChar ' nese eshte TEXT, NTEXT
If FileSize > Threshold Then
' Kemi te bejme me karaktere TEXT/NTEXT, prandaj
' therritet procedura Read2Text, e cila permban metoden
' AppendChunk
Call Read2Text (f, fld, FileSize)
Else
fld.value = Input(FileSize, f)
End If
End Select
Close #f
Exit Sub
err_log:
MsgBox "Gabim ne ruajtjen e te dhenave.", vbOKOnly + vbInformation, App.Title
End Sub
Public Sub Read2Binary(ByVal f As Long, fld As ADODB.Field, _
ByVal FileSize As Long)
' Procedure per te shtuar nje objekt BLOB te tipit IMAGE
Dim Data() As Byte, BytesRead As Long
Do While FileSize <> BytesRead
If FileSize - BytesRead < BLOCK_SIZE Then
Data = InputB(FileSize - BytesRead, f)
BytesRead = FileSize
Else
Data = InputB(BLOCK_SIZE, f)
BytesRead = BytesRead + BLOCK_SIZE
End If
' Shto "llokmat" e imazhit per sa kohe qe
' bajtet e lexuara (BytesRead) jane me te vogla
' se madhesia e skedarit.
Call fld.AppendChunk(Data)
Loop
End Sub
Public Sub Read2Text(ByVal f As Long, fld As ADODB.Field, _
ByVal FileSize As Long)
' Procedure per te shtuar nje objekt BLOB te tipit TEXT/NTEXT
Dim Data As String, CharsRead As Long
Do While FileSize <> CharsRead
If FileSize - CharsRead < BLOCK_SIZE Then
Data = Input(FileSize - CharsRead, f)
CharsRead = FileSize
Else
Data = Input(BLOCK_SIZE, f)
CharsRead = CharsRead + BLOCK_SIZE
End If
' Shto "llokmat" e te dhenave per sa kohe qe
' karakteret e lexuara (CharsRead) jane me te vogla
' se madhesia e skedarit.
Call fld.AppendChunk(Data)
Loop
End Sub
Nxjerrja (Leximi) e te Dhenave BLOB
Kjo procedure eshte pothuaj e anasjellta e procedures se mesiperme. Me pak fjale, dhene nje fushe qe permban te dhena BLOB, keto te transferohen ne nje skedar ne nje shteg te dhene. Skedari krijohet automatikisht, pra, supozohet qe nuk ekziston.
Funksioni BLOB2File transferon informacionin e nje fushe te referuar ne nje skedar ne shteg fizik fale metodes GetChunk(). Ne artikullin perkates tek MS Knowledge Base, BLOB2File eshte dhene si nje procedure SUB. Megjithate, nisur nga disa probleme hasa gjate implementimit, e konvertova ne nje funksion qe kthen nje velre Byte. Funksioni kthen vleren 1 nese objekti BLOB u transferua me sukses ne skedar, dhe vleren 100 nese ndodhi ndonje gabim gjate rruges. Parametrat e funksionit jane:
○ fld - fusha qe i referohet objektit te paracaktuar Field
○ FileName - shtegu dhe emri i skedarit ku do te ruhen te dhenat BLOB
○ FieldSize - madhesia e fushes qe mund te jepet me ane te vecorise ActualSize. Dhenia e vleres se ketij parametri ne menyre eksplicite eshet fakultative, pasi funksioni ne fjale therret nje procedure te vecante kur madhesia e fushes nuk dihet.
○ Threshold - i njejti variabel si ne proceduren File2BLOB
Kodi:
Public Function Blob2File(fld As ADODB.Field, ByVal FName As String, _
Optional FieldSize As Long = -1, _
Optional Threshold As Long = 1048576) As Byte
' Supozime: Skedari fizik nuk ekziston.
' Madhesia e objektit eshte jo me e madhe se 2 Gb.
' Kthen vleren 1 nese nxjerrja e objektit BLOB kaloi me sukses
Dim f As Long, bData() As Byte, sData As String
On Error GoTo err_log
f = FreeFile
Open FName For Binary As #f
Select Case fld.Type
Case adLongVarBinary ' konstantja qe identifikon tipin IMAGE
If FieldSize = -1 Then ' madhesia e fushes nuk eshte dhene/nuk njihet
Call WriteFromUnsizedBinary(f, fld)
Else ' madhesia eshte dhene me ane te ActualSize
If FieldSize > Threshold Then ' madhesia ka kaluar kufirin
Call WriteFromBinary (f, fld, FieldSize)
Else ' te dhena BLOB te vogla
bData = fld.value
Put #f, , bData ' mbingarkese po te perdoret fld.Value ne vend te bData
End If
End If
Case adLongVarChar, adLongVarWChar ' tipet TEXT, NTEXT
If FieldSize = -1 Then
Call WriteFromUnsizedText(f, fld)
Else
If FieldSize > Threshold Then
Call WriteFromText(f, fld, FieldSize)
Else
sData = fld.value
Put #f, , sData ' mbingarkese po te perdoret fld.Value ne vend te sData
End If
End If
End Select
Close #f
Blob2File = 1 ' sukses
Exit Function
err_log:
MsgBox "Gabim ne leximin e plote te te dhenave.", vbOKOnly + vbInformation, App.Title
Close #f
Blob2File = 100 ' gabim
End Function
Public Sub WriteFromBinary(ByVal f As Long, fld As ADODB.Field, _
ByVal FieldSize As Long)
Dim Data() As Byte, BytesRead As Long
Do While FieldSize <> BytesRead
If FieldSize - BytesRead < BLOCK_SIZE Then
Data = fld.GetChunk(FieldSize - BLOCK_SIZE)
BytesRead = FieldSize
Else
Data = fld.GetChunk(BLOCK_SIZE)
BytesRead = BytesRead + BLOCK_SIZE
End If
Put #f, , Data
Loop
End Sub
Public Sub WriteFromUnsizedBinary(ByVal f As Long, fld As ADODB.Field)
Dim Data() As Byte, Temp As Variant
Do
Temp = fld.GetChunk(BLOCK_SIZE)
If IsNull(Temp) Then Exit Do
Data = Temp
Put #f, , Data
Loop While LenB(Temp) = BLOCK_SIZE
End Sub
Public Sub WriteFromText(ByVal f As Long, fld As ADODB.Field, _
ByVal FieldSize As Long)
Dim Data As String, CharsRead As Long
Do While FieldSize <> CharsRead
If FieldSize - CharsRead < BLOCK_SIZE Then
Data = fld.GetChunk(FieldSize - BLOCK_SIZE)
CharsRead = FieldSize
Else
Data = fld.GetChunk(BLOCK_SIZE)
CharsRead = CharsRead + BLOCK_SIZE
End If
Put #f, , Data
Loop
End Sub
Public Sub WriteFromUnsizedText(ByVal f As Long, fld As ADODB.Field)
Dim Data As String, Temp As Variant
Do
Temp = fld.GetChunk(BLOCK_SIZE)
If IsNull(Temp) Then Exit Do
Data = Temp
Put #f, , Data
Loop While Len(Temp) = BLOCK_SIZE
End Sub
Disa Vrojtime Perfundimtare
Konstantet adLongVarBinary, adLongVarChar, dhe adLongVarWChar jane konstante te percaktuara ne ADO per tipet IMAGE, TEXT, dhe NTEXT, perkatesisht. (Germa 'W' ne ADO nenkupton UNICODE, ashtu si germa 'N' ne SQL). Per tipet IMAGE, vecojme perdorimin e funksionit InputB(), i cili perdoret per te lexuar bajte nga nje skedar i dhene. Gjithashtu, procedura Put() shkruan te dhena ne nje skedar te caktuar. Ne rastin e nje objekti BLOB te tipit IMAGE, ne proceduren Put perdoret nje vektor bajtesh, jo drejtperdrejt vlera e fushes, pasi shkakton nje mbingarkese te konsiderueshme gjate shkrimit te te dhenave.
Duhet bere kujdes me perdorimin e vecorise ActualSize te objektit Field kur tentohet te jepet madhesia e nje fushe BLOB ne rastin kur kolona perfaqesuese eshte e tipit TEXT dhe NTEXT. Ne rastin e tipit TEXT, nje karakter ruhet me nje bajt njesi. Ne rastin NTEXT, karakteri ruhet me dy bajte. Per kete arsye, zhvilluesi duhet te gjykoje nese parametri i madhesise duhet te jepet si ActualSize apo si ActualSize/2. Nese nuk behet kujdes, gabimi qe lind thote se "llokma" qe po kerkohet te lexohet eshte e pavlefshme ose e pacaktuar (NULL).
Megjithese kodi i mesiperm kryen punen sakte dhe shpejt, inkorporimi i tij ne pjesen tjeter te sistemit qe po zhvillohet shkakton nje rritje ne shkallen e veshtiresise. Per shembull, supozojme se ne nje tabele te dhene ekziston nje number i caktuar fushash jo BLOB, dhe dy fusha te tjera BLOB te tipeve IMAGE dhe/ose TEXT/NTEXT. Supozojme gjithashtu se ne zhvilluesi ka shkruar nje Stored Procedure qe pranon n+2 parametra me mendimin se do t'i jape procedures te gjitha vlerat e parametrave pernjeheresh. Kjo do te shkaktoje problem, sepse nuk ekziston ndonje menyre e tille; objektet BLOB duhet te kalohen drejtperdrejt nga objekti Field me ane te metodes Update. Pra, procedura SQL duhet modifikuar qe te pranoje te gjithe parametrat e jo BLOB. Fillimisht shtohen vlerat e n parametrave. Me tej, krijohet nje objekt i ri RecordSet, i cili permban vetem rekordin e sapo-shtuar. Me RecordSet-in e ri, identifikojme te dy fushat BLOB dhe ia kalojme procedures File2BLOB. Pasi kjo procedure mbaron me sukses, therrasim metoden Update te fushes perkatese dhe informacioni BLOB shtohet eventualisht. Ketu mund te linde ndonje mbingarkese, sepse databaza "shqetesohet" dy here pa nderprerje. Gjithesesi, bazuar edhe mbi eksperiencen time, kjo procedure eshte nga me te suksesshmet qe ekziston.
Sigurisht, cdo sugjerim ne kod dhe ide eshte i mirepritur.
Krijoni Kontakt