Artigo: 194975 - Última revisão: segunda-feira, 14 de Março de 2005 - Revisão: 3.5

Como ler e escrever BLOBs utilizar GetChunk e AppendChunk

Dica do SistemaEste artigo aplica-se a um sistema operativo diferente do que está a utilizar. Foi desactivado o conteúdo do artigo, que pode não ser relevante para si.
Para obter uma Microsoft Visual Basic .NET versão deste artigo, consulte 317034  (http://support.microsoft.com/kb/317034/EN-US/ ) .
Para obter uma Microsoft Visual Basic .NET versão deste artigo, consulte 316887  (http://support.microsoft.com/kb/316887/EN-US/ ) .

Nesta página

Expandir tudo | Reduzir tudo

Sumário

Este artigo descreve como ler e escrever Binary Large Objects (BLOBs) utilizando métodos GetChunk e AppendChunk com campos de ADO. Também inclui código de exemplo utilizando dados de exemplo NWIND.

Mais Informação

Os métodos GetChunk e AppendChunk trabalham com os LongVarChar LongVarWChar e LongVarBinary coluna tipos, também conhecido como TEXT, NTEXT e IMAGE colunas, no Microsoft SQL Server e como campos MEMO e OLE nas bases de dados do Microsoft Jet. Pode identificar estas colunas no ADO testando a propriedade do tipo de um campo de valores adLongVarChar, adLongVarWChar e adLongVarBinary. Também pode testar a propriedade de atributos de um campo para o sinalizador adFldLong:
If fld.Attributes And adFldLong Then
   ' You can use GetChunk/AppendChunk
				
mais colunas são frequentemente referidas como BLOBs (Binary objectos grandes) mesmo que podem conter dados de texto. O código de exemplo abaixo fornece dois rotinas, BlobToFile e FileToBlob.

BlobToFile

BlobToFile determina o tipo de dados do campo e qual dos três métodos para utilizar para escrever os dados BLOB para um ficheiro no disco. Se os dados BLOB forem suficientemente pequenos, fizer referência ao valor do campo na sua totalidade sem chamar GetChunk. Se o tamanho BLOB for desconhecido, chamará WriteFromUnsizedBinary ou WriteFromUnsizedText para escrever os dados. Este é menos eficiente em termos de fazer cópias adicionais dos dados na memória local do que as rotinas WriteFromBinary e WriteFromText que são utilizados quando o tamanho dos dados BLOB é conhecido:
    BlobToFile                Calls one of the below routines to use GetChunk
    WriteFromBinary         Writes a LongVarBinary of known size to disk
    WriteFromUnsizedBinary  Writes a LongVarBinary on unknown size
    WriteFromText           Writes a LongVarChar of known size
    WriteFromUnsizedText    Writes a LongVarChar of unknown size
				

FileToBlob

FileToBlob determina se deve utilizar AppendChunk ou atribuir directamente os dados para o campo BLOB com base no tamanho do ficheiro. Uma vez que sempre é possível determinar o tamanho do ficheiro, existem não rotinas "Unsized" porque existem no código de exemplo BlobToFile:
    FileToBlob          Calls one of the below routines to use AppendChunk
    ReadToBinary      Reads a file into a LongVarBinary column
    ReadToText        Reads a file into a LongVarChar column
				

Exemplo

O código de exemplo para BlobToFile e FileToBlob é armazenado no módulo, enquanto o código de teste está por trás o formulário predefinido. O código de teste utiliza cada um dos três métodos para guardar no disco de fotografias (IMAGE/OLE/LongVarBinary) e campos (texto/MEMO/LongVarChar/LongVarWChar)-notas de Andrew Fuller da tabela Empregados da base de dados NWIND. É então lê os ficheiros novamente e cria seis novos registos, leia cada uma das três conjuntos de ficheiros através de dois diferentes métodos de leitura. Nota : a utilizar o ADO 2.1 e posterior, poderá ver o seguinte erro na linha de código:
Data = fld.GetChunk(BLOCK_SIZE) :

        Run-time error '94':
        Invalid use of Null
				
ADO 2.1 e posterior pode dar a propriedade ActualSize de um campo BLOB de tipo de texto por duas vezes o número de caracteres. Isto é correcto se o campo BLOB contiver texto Unicode, porque o Unicode utiliza 2 bytes por carácter. Esta é incorrecta se o campo BLOB contém texto em ANSI, que utiliza 1 byte por carácter. Se o ActualSize é duas vezes o comprimento de um campo de ANSI, em seguida, GetChunk eventualmente tenta obter depois do fim do campo.

Pode ver este comportamento nos seguintes cenários e pode vê-la em outros cenários:

cenário 1 :

Com um campo de SQL Server 7 NTEXT (texto ANSI), utilizando ambos o OLE DB Provider para SQL Server ou o fornecedor de ODBC com o controlador de ODBC para SQL Server.

Tenha em atenção que a utilizar o SQL Server texto campo (Unicode) funciona com ambos os fornecedores.

cenário 2 :

Com campos de MEMO do Access 97 e com campos de MEMO do Access 2000 com ou sem compressão Unicode, utilizando ambos o OLE DB Provider para Jet 4.0 ou o fornecedor de ODBC com o controlador ODBC Jet 4.0, ODBCJT32.DLL.

Tenha em atenção que, com campos de MEMO do Access 97, ambas as fornecedor OLE DB Jet 3.51 e o fornecedor de ODBC com o trabalho de controladores Jet ODBC 3.51 correctamente.

Existem várias soluções possíveis para Run-time error '94':
  • Utilizar rs.fieldname.ActualSize \ 2 em vez de rs.fieldname.ActualSize. Este procedimento resolve cada um dos cenários específicos listados acima.

  • Utilize o método 2 ou 3 abaixo, a propriedade ActualSize nenhum dos quais dependem.

A preparar os dados

  1. No Microsoft Access ou outra ferramenta, abra NWIND.MDB.
  2. Abra a tabela Empregados (ou formulário) e localize "Andrew Fuller."
  3. Cola o conteúdo de um ficheiro de texto grande (entre 30000 e 60000 bytes) no campo Notas.
  4. Guardar as alterações e sair do Access.
  5. Adicione uma origem de dados ODBC que aponta para o ficheiro NWIND.MDB.

Código de exemplo

  1. Criar um novo projecto do Visual Basic, no menu Project, seleccione References e seleccione Microsoft ActiveX Data Objects Library ou Microsoft ActiveX Data Objects Library.
  2. Adicione dois CommandButtons (cmdSave e cmdLoad) form(Form1) predefinido.
  3. Adicione o seguinte código. Terá de alterar a cadeia de ligação fornecida na linha "CN.Open":
          Option Explicit
    
          Private Sub CmdSave_Click()
          Dim cn As ADODB.Connection, rs As ADODB.Recordset, SQL As String
            Set cn = New ADODB.Connection
            Set rs = New ADODB.Recordset
            cn.CursorLocation = adUseServer
            cn.Open "dsn=nwind_jet"   ' *** change this ***
            SQL = "SELECT * FROM Employees WHERE LastName='Fuller'"
            rs.Open SQL, cn, adOpenStatic, adLockReadOnly
          '
          ' Save using GetChunk and known size.
          ' FieldSize (ActualSize) > Threshold arg (16384)
          '
            BlobToFile rs!Photo, "c:\photo1.dat", rs!Photo.ActualSize, 16384
            BlobToFile rs!Notes, "c:\notes1.txt", rs!Notes.ActualSize, 16384
             
          ' Uncomment the next line of code, and comment the line above,
          ' to workaround Runtime error '94': Invalid use of Null
          ' BlobToFile rs!Notes, "c:\notes1.txt", rs!Notes.ActualSize \ 2, 16384
    
          '
          ' Save using GetChunk and unknown size.
          ' FieldSize not specified.
          '
            BlobToFile rs!Photo, "c:\photo2.dat"
            BlobToFile rs!Notes, "c:\notes2.txt"
          '
          ' Save without using GetChunk
          ' FieldSize (ActualSize) < Threshold arg (defaults to 1Mb)
          '
            BlobToFile rs!Photo, "c:\photo3.dat", rs!Photo.ActualSize
            BlobToFile rs!Notes, "c:\notes3.txt", rs!Notes.ActualSize
    
          ' Uncomment the next line of code, and comment the line above,
          '   to workaround Runtime error '94': Invalid use of Null
          ' BlobToFile rs!Notes, "c:\notes3.txt", rs!Notes.ActualSize \ 2
    
            rs.Close
            cn.Close
          End Sub
    
          Private Sub CmdLoad_Click()
          Dim cn As ADODB.Connection, rs As ADODB.Recordset, SQL As String
            Set cn = New ADODB.Connection
            Set rs = New ADODB.Recordset
            cn.CursorLocation = adUseServer
            cn.Open "dsn=ole_db_nwind_jet"
            SQL = "SELECT * FROM Employees"
            rs.Open SQL, cn, adOpenKeyset, adLockOptimistic
          '
          ' Load using AppendChunk
          '
            rs.AddNew
            rs!FirstName = "Test"
            rs!LastName = "Fuller11"
            FileToBlob "c:\photo1.dat", rs!Photo, 16384
            FileToBlob "c:\notes1.txt", rs!Notes, 16384
            rs.Update
    
            rs.AddNew
            rs!FirstName = "Test"
            rs!LastName = "Fuller21"
            FileToBlob "c:\photo2.dat", rs!Photo, 16384
            FileToBlob "c:\notes2.txt", rs!Notes, 16384
            rs.Update
    
            rs.AddNew
            rs!FirstName = "Test"
            rs!LastName = "Fuller31"
            FileToBlob "c:\photo3.dat", rs!Photo, 16384
            FileToBlob "c:\notes3.txt", rs!Notes, 16384
            rs.Update
    
          '
          ' Load without using AppendChunk
          '
            rs.AddNew
            rs!FirstName = "Test"
            rs!LastName = "Fuller12"
            FileToBlob "c:\photo1.dat", rs!Photo
            FileToBlob "c:\notes1.txt", rs!Notes
            rs.Update
    
            rs.AddNew
            rs!FirstName = "Test"
            rs!LastName = "Fuller22"
            FileToBlob "c:\photo2.dat", rs!Photo
            FileToBlob "c:\notes2.txt", rs!Notes
            rs.Update
    
            rs.AddNew
            rs!FirstName = "Test"
            rs!LastName = "Fuller32"
            FileToBlob "c:\photo3.dat", rs!Photo
            FileToBlob "c:\notes3.txt", rs!Notes
            rs.Update
    
            rs.Close
            cn.Close
          End Sub
    					
  4. Adicionar um novo módulo ao projecto (Módulo1) com o seguinte código:
          Option Explicit
    
          Const BLOCK_SIZE = 16384
    
          Sub BlobToFile(fld As ADODB.Field, ByVal FName As String, _
                         Optional FieldSize As Long = -1, _
                         Optional Threshold As Long = 1048576)
          '
          ' Assumes file does not exist
          ' Data cannot exceed approx. 2Gb in size
          '
          Dim F As Long, bData() As Byte, sData As String
            F = FreeFile
            Open FName For Binary As #F
            Select Case fld.Type
              Case adLongVarBinary
                If FieldSize = -1 Then   ' blob field is of unknown size
                  WriteFromUnsizedBinary F, fld
                Else                     ' blob field is of known size
                  If FieldSize > Threshold Then   ' very large actual data
                    WriteFromBinary F, fld, FieldSize
                  Else                            ' smallish actual data
                    bData = fld.Value
                    Put #F, , bData  ' PUT tacks on overhead if use fld.Value
                  End If
                End If
              Case adLongVarChar, adLongVarWChar
                If FieldSize = -1 Then
                  WriteFromUnsizedText F, fld
                Else
                  If FieldSize > Threshold Then
                    WriteFromText F, fld, FieldSize
                  Else
                    sData = fld.Value
                    Put #F, , sData  ' PUT tacks on overhead if use fld.Value
                  End If
                End If
            End Select
            Close #F
          End Sub
    
          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
    
          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
    
          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
    
          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
    
          Sub FileToBlob(ByVal FName As String, fld As ADODB.Field, _
                         Optional Threshold As Long = 1048576)
          '
          ' Assumes file exists
          ' Assumes calling routine does the UPDATE
          ' File cannot exceed approx. 2Gb in size
          '
          Dim F As Long, Data() As Byte, FileSize As Long
            F = FreeFile
            Open FName For Binary As #F
            FileSize = LOF(F)
            Select Case fld.Type
              Case adLongVarBinary
                If FileSize > Threshold Then
                  ReadToBinary F, fld, FileSize
                Else
                  Data = InputB(FileSize, F)
                  fld.Value = Data
                End If
              Case adLongVarChar, adLongVarWChar
                If FileSize > Threshold Then
                  ReadToText F, fld, FileSize
                Else
                  fld.Value = Input(FileSize, F)
                End If
            End Select
            Close #F
          End Sub
    
          Sub ReadToBinary(ByVal F As Long, fld As ADODB.Field, _
                           ByVal FileSize As Long)
          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
              fld.AppendChunk Data
            Loop
          End Sub
    
          Sub ReadToText(ByVal F As Long, fld As ADODB.Field, _
                         ByVal FileSize As Long)
          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
              fld.AppendChunk Data
            Loop
          End Sub
    					
  5. Execute o projecto e clique no botão cmdSave.
  6. No directório C:\, deverá encontrar os seguintes ficheiros: notes1.txt
    notes2.txt
    notes3.txt

    photo1.dat
    photo2.dat
    photo3.dat

    Os três ficheiros "Fotografia" devem ter o mesmo tamanho como entre si. Os três ficheiros "notas" devem ter o mesmo tamanho como entre si.

  7. Clique no botão cmdLoad.
  8. Abrir base de dados utilizando acesso e deverá ver seis empregados adicionais com fotografias e notas novamente carregadas correctamente.

Notas

Seguem-se algumas sugestões para utilizar BLOBs com o ADO. Estes paralelas muitas das sugestões no seguinte artigo da base de dados de conhecimento da Microsoft:
153238  (http://support.microsoft.com/kb/153238/EN-US/ ) Como utilizar GetChunk e AppendChunk métodos do objecto RDO
  1. É mais eficiente em termos de obtenção de dados BLOB simplesmente armazenar os dados nos ficheiros no servidor com um ponteiro no registo principal (ou pode utilizar algum tipo de directório/ficheiro estruturado sistema baseado no valor de chave primária de nomenclatura). Isto tem a vantagem de (a) eliminando a carga do servidor, (b) permitindo que os ficheiros sejam armazenados num segundo servidor, (c) permitindo rede atributos de segurança a ser definido em ficheiros individuais e (d) permitindo a obtenção de ficheiros mesmo quando o servidor esteja desligado. Isto é especialmente verdade se os ficheiros são algum tipo de tipo de documento, tal como mapas de bits (.bmp), ficheiros de processador (.doc) ou folhas de cálculo (.xls) onde pode apontar a aplicação anfitriã directamente para o ficheiro no servidor.
  2. Quando utilizar alguns fornecedores, nomeadamente ODBC para SQL Server e outras bases de dados, poderá ter de tomar atenção especial em obter dados BLOB, como, por exemplo, colocar BLOB colunas no fim da lista de campos e referência todos os campos não BLOB antes acesso BLOB colunas. Isto dependerá de diversos factores, tais como:
    • Fornecedor (normalmente ODBC)
    • Servidor back-end
    • Localização do cursor (normalmente cliente)
    • Tipo de cursor
    • Se estiver a seleccionar VIEW ou obter registos devolvidos a partir de um procedimento armazenado.
Porque depende de vários factores, segue-se uma guia se ocorrerem problemas com BLOB colunas:
  • Tente um fornecedor de OLE DB nativo em vez de um fornecedor ODBC.
  • Utilize cursores do lado do servidor (por exemplo, adOpenKeyset).
  • Seleccione as colunas chave primária em conjunto com outras colunas.
  • Seleccione as colunas BLOB pela última vez. Seleccione campos individuais, não "*".
  • Aceder a todas as colunas não BLOB primeiro (armazená-las se necessário).
  • Acesso BLOB colunas pela ordem especificada. Só poderá referenciar uma vez antes do cursor perde o respectivo valor.
  • Quando editar uma coluna BLOB utilizando o método AppendChunk, poderá ter de editar, pelo menos, uma coluna BLOB não bem o conjunto de registos. BLOBs normalmente não são actualizáveis com estático ou cursores só de avanço no ODBC datasources.
  • Se utilizar o ODBC para Jet, não pode actualizar um conjunto de registos devolvido por uma procedimentos armazenados (QueryDef) sequer porque força o controlador para ser só de leitura.
  • O Microsoft Oracle fornecedor de OLE DB não suporta actualmente aleatório acesso a dados BLOB com cursores do lado do servidor - coluna BLOB tem de aparecer para o fim da cláusula SELECT.
Com a biblioteca de cursores ODBC não é possível utilizar os métodos GetChunk ou AppendChunk num conjunto de registos devolvido por um procedimento armazenado. Isto acontece porque os BLOB não é normalmente obter dados com os restantes dados para poupar largura de banda. Quando um procedimento armazenado cria um conjunto de registos, o controlador de cursor não é possível determinar como consultar os dados BLOB depois do facto porque não é possível determinar as tabelas base ou campos de chave a utilizar. Cursores do lado do servidor minimizar este problema mas limitam a uma única instrução por procedimento armazenado (uma restrição de SQL Server).

O facto de que os utilizadores pretenderem actualizar a respectiva coluna BLOB exige que expõem as respectivas tabelas base e criar o cursor utilizando uma instrução select padrão dessa tabela base. Isto seria VERDADEIRO mesmo se foram codificação directamente para ODBC (não um item de ADO).

Referências

Para obter informações adicionais, consulte os seguintes artigos na base de dados de conhecimento da Microsoft:
185958  (http://support.microsoft.com/kb/185958/EN-US/ ) Como utilizar ADO GetChunk/AppendChunk com Oracle para dados BLOB
189415  (http://support.microsoft.com/kb/189415/EN-US/ ) FICHEIRO: AdoChunk.exe utilizando GetChunk e AppendChunk no Visual C++
Utilizar objectos de acesso a dados:
103257  (http://support.microsoft.com/kb/103257/EN-US/ ) ACC: Ler, armazenar & Writing Binary Large Objects (BLOBs)

A informação contida neste artigo aplica-se a:
  • Microsoft ActiveX Data Objects 1.5
  • Microsoft ActiveX Data Objects 2.0
  • Microsoft ActiveX Data Objects 2.1 Service Pack 2
  • Microsoft ActiveX Data Objects 2.5
  • Microsoft ActiveX Data Objects 2.6
  • Microsoft ActiveX Data Objects 2.7
  • Microsoft Visual Basic 6.0 Professional Edition
  • Microsoft Visual Basic Enterprise Edition for Windows 6.0
Palavras-chave: 
kbmt kbbug kbdatabase kbhowto KB194975 KbMtpt
Tradução automáticaTradução automática
IMPORTANTE: Este artigo foi traduzido por um sistema de tradução automática (também designado por Machine translation ou MT), não tendo sido portanto revisto ou traduzido por humanos. A Microsoft tem artigos traduzidos por aplicações (MT) e artigos traduzidos por tradutores profissionais. O objectivo é simples: oferecer em Português a totalidade dos artigos existentes na base de dados do suporte. Sabemos no entanto que a tradução automática não é sempre perfeita. Esta pode conter erros de vocabulário, sintaxe ou gramática? erros semelhantes aos que um estrangeiro realiza ao falar em Português. A Microsoft não é responsável por incoerências, erros ou estragos realizados na sequência da utilização dos artigos MT por parte dos nossos clientes. A Microsoft realiza actualizações frequentes ao software de tradução automática (MT). Obrigado.
Clique aqui para ver a versão em Inglês deste artigo: 194975  (http://support.microsoft.com/kb/194975/en-us/ )