Esse problema costuma aparecer periodicamente no news: como fazer para recuperar o conteúdo de uma página via XMLHTTP sem perder os caracteres acentuados ? O comportamento que é reportado se dá por conta de como os dados são transmitidos via XMLHTTP. Quando uma solicitação é feita, é aberta uma conexão com a página em questão e o conteúdo dela é retornado como uma sequência Unicode, onde cada caracter é representado em dois bytes. "Que disperdício!", você poderia pensar, mas se pensarmos que o ASCII permite representar 256 caracteres, a tabela Unicode nos permite representar 65536 caracteres (256^2), o que é muito mais interessante na Web, onde diversos idiomas coexistem e nos abre a possibilidade de explorar o potencial de internacionalização de documentos.
Voltando ao ponto, se há uma indicação que os dados que estão sendo transmitidos são XML (em outras palavras, se o Content-Type está setado para text/xml), a propriedade responseXML do XMLHTTP interpreta o encoding do documento e, caso o conteúdo do mesmo esteja bem formado, retorna o resultado na forma de um objeto DOMDocument. Caso não esteja bem formado, ou não se trate de um documento XML, o XMLHTTP vai assumir que o documento está codificado em UTF-8 e a propriedade responseText vai retornar algo esquisito: os acentos são substituídos por um ponto de interrogação e a próxima letra é perdida. Isso aconteceu na tentativa de transformar os caracteres recebidos (Unicode) para ASCII, representável no seu computador.
A "brilhante" conclusão que chegamos até aqui é que, caso estejamos transmitindo um documento XML, basta definirmos qual o encoding que está sendo utilizado (usando a famosa processing instruction <?xml version="1.0" encoding="ISO-8859-1"?> ) e que, para documentos codificados em UTF-8, as coisas funcionam bem. Isso exclui os documentos escritos em português, por exemplo, pois os caracteres acentuados fazem parte do ISO-8859-1, excedendo o UTF-8.
Isso não significa que é impossível. Há outras formas de acessar o conteúdo recebido do XMLHTTP. São elas:
- responseStream
- responseBody
A primeira, sinceramente, não consegui fazer funcionar. Mas fatalmente cairia na necessidade de algum objeto ADODB para fazer a conversão, como o Recordset ou Stream.
Já o responseBody, foi bastante útil. Segundo a documentação da Microsoft "... representa como resposta a entidade body como uma array de unsigned bytes (...) Contém os bytes não decodificados, da forma que foram recebidos pelo servidor." lembrei-me então de uma rotina que acompanha o código de upload sem componentes, que faz exatamente esse tipo de conversão. Juntando as idéias, fiquei com o código abaixo:
<%
set oXMLHTTP = Server.CreateObject("Microsoft.XMLHTTP")
oXMLHTTP.open "GET", "http://localhost/conteudo.asp", false
oXMLHTTP.send
response.write "<xmp>" & oXMLHTTP.getAllResponseHeaders & "</xmp>" & _
oXMLHTTP.responseText & "<br />" & _
BinaryToString( oXMLHTTP.responseBody ) & "<br />"
'Baseado no código de Philippe Collignon (PhCollignon@email.com)
Function BinaryToString(strBinary)
Dim intCount
BinaryToString =""
For intCount = 1 to LenB(strBinary)
BinaryToString = BinaryToString & chr(AscB(MidB(strBinary,intCount,1)))
Next
End Function
%>
Pesquisando mais um pouco, descobri que o código da BinaryToString pode ser otimizado. Ao invés de chamá-la, deveriamos substituí-la pela RSBinaryToString, desenvolvida por Antonin Foller @ PStruh, cujo fonte segue abaixo:
<%
Function RSBinaryToString(xBinary)
Dim Binary
If VarType(xBinary)=8 Then Binary = MultiByteToBinary(xBinary) Else Binary = xBinary
Dim RS, LBinary
Const adLongVarChar = 201
Set RS = CreateObject("ADODB.Recordset")
LBinary = LenB(Binary)
If LBinary>0 Then
RS.Fields.Append "mBinary", adLongVarChar, LBinary
RS.Open
RS.AddNew
RS("mBinary").AppendChunk Binary
RS.Update
RSBinaryToString = RS("mBinary")
Set RS=Nothing
Else
RSBinaryToString = ""
End If
Set RS=Nothing
End Function
Function MultiByteToBinary(MultiByte)
Dim RS, LMultiByte, Binary
Const adLongVarBinary = 205
Set RS = CreateObject("ADODB.Recordset")
LMultiByte = LenB(MultiByte)
If LMultiByte>0 Then
RS.Fields.Append "mBinary", adLongVarBinary, LMultiByte
RS.Open
RS.AddNew
RS("mBinary").AppendChunk MultiByte & ChrB(0)
RS.Update
Binary = RS("mBinary").GetChunk(LMultiByte)
End If
Set RS = Nothing
MultiByteToBinary = Binary
End Function
%>
Finalmente, a página conteudo.asp contém o seguinte código:
<%
'Evitar que as páginas sejam mantidas em cache
Response.AddHeader "Pragma", "No-Cache"
Response.CacheControl = "private"
Response.AddHeader "cache-control", "private"
Response.Expires = -1
Response.ExpiresAbsolute=#Jan 01,1990 00:00:01#
Response.Buffer = False
%>
áéíóú<%= timer %>àèìòù
.
Desta forma, os caracteres são convertidos para uma sequência digesta, permitindo que o conteúdo possa ser acessado em toda sua riqueza. Em outras palavras, os acentos são retornados corretamente.
Divirta-se!
1 comentários:
[Enviado originalmente em 16/set/2002 22:20:24]:
Belíssima explicação da causa e sua correção.
Curvo-me aos teus pés meu Guru!
Postar um comentário