idHTTP 中的字符编码错误
Character encoding error in idHTTP
我遇到了 TIdHTTP 和 TIdMultipartFormDataStream 的情况。
我的代码是:
FormPHP := TIdMultiPartFormDataStream.Create;
FormPHP.AddFile('imagem',AImagem,'image/jpeg');
FormPHP.AddFormField('iduser',AIDUser,'text/plain');
FormPHP.AddFormField('nome',ANome,'text/plain');
FormPHP.AddFormField('data',AData,'text/plain');
FormPHP.AddFormField('hora',AHora,'text/plain');
FormPHP.AddFormField('mensagem',AMensagem,'text/plain');
FormPHP.AddFormField('latitude','1','text/plain');
FormPHP.AddFormField('longitude','1','text/plain');
Response := TStringStream.Create('', TEncoding.ANSI);
HTTP:= TIdHTTP.Create(self);
HTTP.Request.CustomHeaders.Clear;
HTTP.Request.Clear;
HTTP.Request.ContentType:= 'multipart/form-data'; //application/x-www-form-urlencoded
HTTP.Request.ContentEncoding:= 'MeMIME';
HTTP.Request.CharSet:= 'utf-8';
HTTP.Request.Referer:= 'http://observadordecascavel.blog.br/cadastro.php';
HTTP.Post('http://observadordecascavel.blog.br/cadastro.php',FormPHP,Response);
这是 PHP 脚本:
<?php
#cadastro.php - Cadastra os dados enviados na tabela online.
$mysqli = new mysqli("mysqlhost","username","password","dbname");
$iduser = $_POST['iduser'];
$nome = $_POST['nome'];
$data = $_POST['data'];
$hora = $_POST['hora'];
$mensagem = $_POST['mensagem'];
$latitude = $_POST['latitude'];
$longitude = $_POST['longitude'];
$imagem = $_FILES["imagem"]['tmp_name'];
$tamanho = $_FILES['imagem']['size'];
if ( $imagem != "none" )
{
$fp = fopen($imagem, "rb");
$conteudo = fread($fp, $tamanho);
$conteudo = addslashes($conteudo);
fclose($fp);
$queryInsercao = "INSERT INTO tabpainel (iduser, nome, data, hora, mensagem, latitude, longitude, imagem) VALUES ('$iduser', '$nome', '$data','$hora','$mensagem', '$latitude', '$longitude', '$conteudo')";
mysqli_query($mysqli,$queryInsercao) or die("Algo deu errado ao inserir o registro. Tente novamente.");
if(mysqli_affected_rows($mysqli) > 0)
print "Sucesso!";
else
print "Não foi possível inserir o registro";
}
else
print "Não á foi possível carregar a imagem.";
?>
解释:我的应用程序 post 此 PHP 脚本的这些字段和 php 将数据保存到 MySQL 数据库和 returns 响应"Sucesso!" 到应用程序以通知用户数据已保存。此文本响应以 ANSI 编码。我发现当我不得不将 TStringStream 编码更改为 TEncoding.ANSI 时,它可以在出现问题时识别 "Não" 单词。
直到post,变量AMensagem是可以的,但是,当PHP收到文本时,它是不正确的。像这样的文本:“á Á é É”看起来像这样“=E1 =C1 =E9 =C9”。这保存在 mysql 数据库中。
我不知道问题出在 idHTTP 还是 TIdMultipartFormDataStream,甚至是 PHP 代码。一切正常,只是编码我不知道为什么它不起作用。
传输到服务器的文本未使用 UTF-8 编码。
您的所有 AddFormField()
调用都在 ACharset
参数而不是 AContentType
参数中指定 text/plain
媒体类型。与AddFile()
不同的是,AddFormField()
的第3个参数是字符集,第4个参数是媒体类型。
function AddFormField(const AFieldName, AFieldValue: string; const ACharset: string = ''; const AContentType: string = ''; const AFileName: string = ''): TIdFormDataField; overload;
通过传递无效的字符集,TIdMultipartFormDataStream
最终使用 Indy 的内置原始 8 位编码,它将 Unicode 字符 U+0000 - U+00FF
分别编码为字节 [=29=] - $FF
,以及所有其他字符字符作为字节 F
('?'
)。您发送的文本 恰好 属于第一个范围。
TIdFormDataField
当前不从 TIdMultipartFormDataStream
或 TIdHTTP
继承字符集(相关工作正在进行中),因此您必须在每个字段的基础上指定它.
附带说明,MeMIME
不是有效的 ContentEncoding
值。而且您不应该为 multipart/form-data
post 设置任何 ContentEncoding
值。
尝试更像这样的东西:
FormPHP := TIdMultiPartFormDataStream.Create;
FormPHP.AddFile('imagem', AImagem, 'image/jpeg');
FormPHP.AddFormField('iduser', AIDUser, 'utf-8');
FormPHP.AddFormField('nome', ANome, 'utf-8');
FormPHP.AddFormField('data', AData, 'utf-8');
FormPHP.AddFormField('hora', AHora, 'utf-8');
FormPHP.AddFormField('mensagem', AMensagem, 'utf-8');
FormPHP.AddFormField('latitude', '1');
FormPHP.AddFormField('longitude', '1');
Response := TStringStream.Create('');
HTTP := TIdHTTP.Create(Self);
HTTP.Request.Referer := 'http://observadordecascavel.blog.br/cadastro.php';
HTTP.Post('http://observadordecascavel.blog.br/cadastro.php', FormPHP, Response);
或者:
FormPHP := TIdMultiPartFormDataStream.Create;
FormPHP.AddFile('imagem', AImagem, 'image/jpeg');
FormPHP.AddFormField('iduser', AIDUser).Charset := 'utf-8';
FormPHP.AddFormField('nome', ANome).Charset := 'utf-8';
FormPHP.AddFormField('data', AData).Charset := 'utf-8';
FormPHP.AddFormField('hora', AHora).Charset := 'utf-8';
FormPHP.AddFormField('mensagem', AMensagem).Charset := 'utf-8';
FormPHP.AddFormField('latitude', '1');
FormPHP.AddFormField('longitude', '1');
Response := TStringStream.Create('');
HTTP := TIdHTTP.Create(Self);
HTTP.Request.Referer := 'http://observadordecascavel.blog.br/cadastro.php';
HTTP.Post('http://observadordecascavel.blog.br/cadastro.php', FormPHP, Response);
无论哪种方式,字段文本都将使用 UTF-8 而不是 Ansi 进行编码。
更新:现在,话虽如此,AddFormField()
默认将 TIdFormDataField.ContentTransfer
属性 设置为 quoted-printable
。但是,PHP 的 $_POST
默认不解码 quoted-printable
,您必须手动调用 quoted_printable_decode()
:
$iduser = quoted_printable_decode($_POST['iduser']);
$nome = quoted_printable_decode($_POST['nome']);
$data = quoted_printable_decode($_POST['data']);
$hora = quoted_printable_decode($_POST['hora']);
$mensagem = quoted_printable_decode($_POST['mensagem']);
$latitude = quoted_printable_decode($_POST['latitude']);
$longitude = quoted_printable_decode($_POST['longitude']);
如果您不想TIdFormDataField
使用quoted-printable
对UTF-8文本进行编码,您可以将ContentTransfer
属性设置为8bit
相反:
FormPHP.AddFormField('iduser', AIDUser, 'utf-8').ContentTransfer := '8bit';
FormPHP.AddFormField('nome', ANome, 'utf-8').ContentTransfer := '8bit';
FormPHP.AddFormField('data', AData, 'utf-8').ContentTransfer := '8bit';
FormPHP.AddFormField('hora', AHora, 'utf-8').ContentTransfer := '8bit';
FormPHP.AddFormField('mensagem', AMensagem, 'utf-8').ContentTransfer := '8bit';
FormPHP.AddFormField('latitude', '1');
FormPHP.AddFormField('longitude', '1');
或者:
with FormPHP.AddFormField('iduser', AIDUser) do begin
Charset := 'utf-8';
ContentTransfer := '8bit';
end;
with FormPHP.AddFormField('nome', ANome) do begin
Charset := 'utf-8';
ContentTransfer := '8bit';
end;
with FormPHP.AddFormField('data', AData) do begin
Charset := 'utf-8';
ContentTransfer := '8bit';
end;
with FormPHP.AddFormField('hora', AHora) do begin
Charset := 'utf-8';
ContentTransfer := '8bit';
end;
with FormPHP.AddFormField('mensagem', AMensagem) do begin
Charset := 'utf-8';
ContentTransfer := '8bit';
end;
FormPHP.AddFormField('latitude', '1');
FormPHP.AddFormField('longitude', '1');
无论哪种方式,您都可以再次使用原来的 PHP 代码:
$iduser = $_POST['iduser'];
$nome = $_POST['nome'];
$data = $_POST['data'];
$hora = $_POST['hora'];
$mensagem = $_POST['mensagem'];
$latitude = $_POST['latitude'];
$longitude = $_POST['longitude'];
无论您是否使用 quoted-printable
,PHP 变量最终都会保存 UTF-8 编码的文本。如果您需要变量采用另一种编码,则必须根据需要使用以下任一方式转换它们:
utf8_decode()
(解码为 ISO-8859-1):
$iduser = utf8_decode($iduser);
$nome = utf8_decode($nome);
$data = utf8_decode($data);
$hora = utf8_decode($hora);
$mensagem = utf8_decode($mensagem);
$latitude = utf8_decode($latitude);
$longitude = utf8_decode($longitude);
-
$iduser = mb_convert_encoding($iduser, 'desired charset', 'utf-8');
$nome = mb_convert_encoding($nome), 'desired charset', 'utf-8');
$data = mb_convert_encoding($data, 'desired charset', 'utf-8');
$hora = mb_convert_encoding($hora, 'desired charset', 'utf-8');
$mensagem = mb_convert_encoding($mensagem, 'desired charset', 'utf-8');
$latitude = mb_convert_encoding($latitude, 'desired charset', 'utf-8');
$longitude = mb_convert_encoding($longitude, 'desired charset', 'utf-8');
-
$iduser = iconv('utf-8', 'desired charset', $iduser);
$nome = iconv('utf-8', 'desired charset', $nome);
$data = iconv('utf-8', 'desired charset', $data);
$hora = iconv('utf-8', 'desired charset', $hora);
$mensagem = iconv('utf-8', 'desired charset', $mensagem);
$latitude = iconv('utf-8', 'desired charset', $latitude);
$longitude = iconv('utf-8', 'desired charset', $longitude);
最后,当向客户端发送响应时,您需要对包含非 ASCII 字符的文本进行编码。您还应该使用 header()
让客户端知道该编码使用了哪个字符集:
header($_SERVER["SERVER_PROTOCOL"] . " 200 OK");
header('Content-Type: text/plain; charset="utf-8"');
if ( $imagem != "none" )
{
...
if (mysqli_affected_rows($mysqli) > 0)
print utf8_encode("Sucesso!");
else
print utf8_encode("Não foi possível inserir o registro");
}
else
print utf8_encode("Não á foi possível carregar a imagem.");
我遇到了 TIdHTTP 和 TIdMultipartFormDataStream 的情况。
我的代码是:
FormPHP := TIdMultiPartFormDataStream.Create;
FormPHP.AddFile('imagem',AImagem,'image/jpeg');
FormPHP.AddFormField('iduser',AIDUser,'text/plain');
FormPHP.AddFormField('nome',ANome,'text/plain');
FormPHP.AddFormField('data',AData,'text/plain');
FormPHP.AddFormField('hora',AHora,'text/plain');
FormPHP.AddFormField('mensagem',AMensagem,'text/plain');
FormPHP.AddFormField('latitude','1','text/plain');
FormPHP.AddFormField('longitude','1','text/plain');
Response := TStringStream.Create('', TEncoding.ANSI);
HTTP:= TIdHTTP.Create(self);
HTTP.Request.CustomHeaders.Clear;
HTTP.Request.Clear;
HTTP.Request.ContentType:= 'multipart/form-data'; //application/x-www-form-urlencoded
HTTP.Request.ContentEncoding:= 'MeMIME';
HTTP.Request.CharSet:= 'utf-8';
HTTP.Request.Referer:= 'http://observadordecascavel.blog.br/cadastro.php';
HTTP.Post('http://observadordecascavel.blog.br/cadastro.php',FormPHP,Response);
这是 PHP 脚本:
<?php
#cadastro.php - Cadastra os dados enviados na tabela online.
$mysqli = new mysqli("mysqlhost","username","password","dbname");
$iduser = $_POST['iduser'];
$nome = $_POST['nome'];
$data = $_POST['data'];
$hora = $_POST['hora'];
$mensagem = $_POST['mensagem'];
$latitude = $_POST['latitude'];
$longitude = $_POST['longitude'];
$imagem = $_FILES["imagem"]['tmp_name'];
$tamanho = $_FILES['imagem']['size'];
if ( $imagem != "none" )
{
$fp = fopen($imagem, "rb");
$conteudo = fread($fp, $tamanho);
$conteudo = addslashes($conteudo);
fclose($fp);
$queryInsercao = "INSERT INTO tabpainel (iduser, nome, data, hora, mensagem, latitude, longitude, imagem) VALUES ('$iduser', '$nome', '$data','$hora','$mensagem', '$latitude', '$longitude', '$conteudo')";
mysqli_query($mysqli,$queryInsercao) or die("Algo deu errado ao inserir o registro. Tente novamente.");
if(mysqli_affected_rows($mysqli) > 0)
print "Sucesso!";
else
print "Não foi possível inserir o registro";
}
else
print "Não á foi possível carregar a imagem.";
?>
解释:我的应用程序 post 此 PHP 脚本的这些字段和 php 将数据保存到 MySQL 数据库和 returns 响应"Sucesso!" 到应用程序以通知用户数据已保存。此文本响应以 ANSI 编码。我发现当我不得不将 TStringStream 编码更改为 TEncoding.ANSI 时,它可以在出现问题时识别 "Não" 单词。
直到post,变量AMensagem是可以的,但是,当PHP收到文本时,它是不正确的。像这样的文本:“á Á é É”看起来像这样“=E1 =C1 =E9 =C9”。这保存在 mysql 数据库中。
我不知道问题出在 idHTTP 还是 TIdMultipartFormDataStream,甚至是 PHP 代码。一切正常,只是编码我不知道为什么它不起作用。
传输到服务器的文本未使用 UTF-8 编码。
您的所有 AddFormField()
调用都在 ACharset
参数而不是 AContentType
参数中指定 text/plain
媒体类型。与AddFile()
不同的是,AddFormField()
的第3个参数是字符集,第4个参数是媒体类型。
function AddFormField(const AFieldName, AFieldValue: string; const ACharset: string = ''; const AContentType: string = ''; const AFileName: string = ''): TIdFormDataField; overload;
通过传递无效的字符集,TIdMultipartFormDataStream
最终使用 Indy 的内置原始 8 位编码,它将 Unicode 字符 U+0000 - U+00FF
分别编码为字节 [=29=] - $FF
,以及所有其他字符字符作为字节 F
('?'
)。您发送的文本 恰好 属于第一个范围。
TIdFormDataField
当前不从 TIdMultipartFormDataStream
或 TIdHTTP
继承字符集(相关工作正在进行中),因此您必须在每个字段的基础上指定它.
附带说明,MeMIME
不是有效的 ContentEncoding
值。而且您不应该为 multipart/form-data
post 设置任何 ContentEncoding
值。
尝试更像这样的东西:
FormPHP := TIdMultiPartFormDataStream.Create;
FormPHP.AddFile('imagem', AImagem, 'image/jpeg');
FormPHP.AddFormField('iduser', AIDUser, 'utf-8');
FormPHP.AddFormField('nome', ANome, 'utf-8');
FormPHP.AddFormField('data', AData, 'utf-8');
FormPHP.AddFormField('hora', AHora, 'utf-8');
FormPHP.AddFormField('mensagem', AMensagem, 'utf-8');
FormPHP.AddFormField('latitude', '1');
FormPHP.AddFormField('longitude', '1');
Response := TStringStream.Create('');
HTTP := TIdHTTP.Create(Self);
HTTP.Request.Referer := 'http://observadordecascavel.blog.br/cadastro.php';
HTTP.Post('http://observadordecascavel.blog.br/cadastro.php', FormPHP, Response);
或者:
FormPHP := TIdMultiPartFormDataStream.Create;
FormPHP.AddFile('imagem', AImagem, 'image/jpeg');
FormPHP.AddFormField('iduser', AIDUser).Charset := 'utf-8';
FormPHP.AddFormField('nome', ANome).Charset := 'utf-8';
FormPHP.AddFormField('data', AData).Charset := 'utf-8';
FormPHP.AddFormField('hora', AHora).Charset := 'utf-8';
FormPHP.AddFormField('mensagem', AMensagem).Charset := 'utf-8';
FormPHP.AddFormField('latitude', '1');
FormPHP.AddFormField('longitude', '1');
Response := TStringStream.Create('');
HTTP := TIdHTTP.Create(Self);
HTTP.Request.Referer := 'http://observadordecascavel.blog.br/cadastro.php';
HTTP.Post('http://observadordecascavel.blog.br/cadastro.php', FormPHP, Response);
无论哪种方式,字段文本都将使用 UTF-8 而不是 Ansi 进行编码。
更新:现在,话虽如此,AddFormField()
默认将 TIdFormDataField.ContentTransfer
属性 设置为 quoted-printable
。但是,PHP 的 $_POST
默认不解码 quoted-printable
,您必须手动调用 quoted_printable_decode()
:
$iduser = quoted_printable_decode($_POST['iduser']);
$nome = quoted_printable_decode($_POST['nome']);
$data = quoted_printable_decode($_POST['data']);
$hora = quoted_printable_decode($_POST['hora']);
$mensagem = quoted_printable_decode($_POST['mensagem']);
$latitude = quoted_printable_decode($_POST['latitude']);
$longitude = quoted_printable_decode($_POST['longitude']);
如果您不想TIdFormDataField
使用quoted-printable
对UTF-8文本进行编码,您可以将ContentTransfer
属性设置为8bit
相反:
FormPHP.AddFormField('iduser', AIDUser, 'utf-8').ContentTransfer := '8bit';
FormPHP.AddFormField('nome', ANome, 'utf-8').ContentTransfer := '8bit';
FormPHP.AddFormField('data', AData, 'utf-8').ContentTransfer := '8bit';
FormPHP.AddFormField('hora', AHora, 'utf-8').ContentTransfer := '8bit';
FormPHP.AddFormField('mensagem', AMensagem, 'utf-8').ContentTransfer := '8bit';
FormPHP.AddFormField('latitude', '1');
FormPHP.AddFormField('longitude', '1');
或者:
with FormPHP.AddFormField('iduser', AIDUser) do begin
Charset := 'utf-8';
ContentTransfer := '8bit';
end;
with FormPHP.AddFormField('nome', ANome) do begin
Charset := 'utf-8';
ContentTransfer := '8bit';
end;
with FormPHP.AddFormField('data', AData) do begin
Charset := 'utf-8';
ContentTransfer := '8bit';
end;
with FormPHP.AddFormField('hora', AHora) do begin
Charset := 'utf-8';
ContentTransfer := '8bit';
end;
with FormPHP.AddFormField('mensagem', AMensagem) do begin
Charset := 'utf-8';
ContentTransfer := '8bit';
end;
FormPHP.AddFormField('latitude', '1');
FormPHP.AddFormField('longitude', '1');
无论哪种方式,您都可以再次使用原来的 PHP 代码:
$iduser = $_POST['iduser'];
$nome = $_POST['nome'];
$data = $_POST['data'];
$hora = $_POST['hora'];
$mensagem = $_POST['mensagem'];
$latitude = $_POST['latitude'];
$longitude = $_POST['longitude'];
无论您是否使用 quoted-printable
,PHP 变量最终都会保存 UTF-8 编码的文本。如果您需要变量采用另一种编码,则必须根据需要使用以下任一方式转换它们:
utf8_decode()
(解码为 ISO-8859-1):$iduser = utf8_decode($iduser); $nome = utf8_decode($nome); $data = utf8_decode($data); $hora = utf8_decode($hora); $mensagem = utf8_decode($mensagem); $latitude = utf8_decode($latitude); $longitude = utf8_decode($longitude);
-
$iduser = mb_convert_encoding($iduser, 'desired charset', 'utf-8'); $nome = mb_convert_encoding($nome), 'desired charset', 'utf-8'); $data = mb_convert_encoding($data, 'desired charset', 'utf-8'); $hora = mb_convert_encoding($hora, 'desired charset', 'utf-8'); $mensagem = mb_convert_encoding($mensagem, 'desired charset', 'utf-8'); $latitude = mb_convert_encoding($latitude, 'desired charset', 'utf-8'); $longitude = mb_convert_encoding($longitude, 'desired charset', 'utf-8');
-
$iduser = iconv('utf-8', 'desired charset', $iduser); $nome = iconv('utf-8', 'desired charset', $nome); $data = iconv('utf-8', 'desired charset', $data); $hora = iconv('utf-8', 'desired charset', $hora); $mensagem = iconv('utf-8', 'desired charset', $mensagem); $latitude = iconv('utf-8', 'desired charset', $latitude); $longitude = iconv('utf-8', 'desired charset', $longitude);
最后,当向客户端发送响应时,您需要对包含非 ASCII 字符的文本进行编码。您还应该使用 header()
让客户端知道该编码使用了哪个字符集:
header($_SERVER["SERVER_PROTOCOL"] . " 200 OK");
header('Content-Type: text/plain; charset="utf-8"');
if ( $imagem != "none" )
{
...
if (mysqli_affected_rows($mysqli) > 0)
print utf8_encode("Sucesso!");
else
print utf8_encode("Não foi possível inserir o registro");
}
else
print utf8_encode("Não á foi possível carregar a imagem.");