gpt4 book ai didi

delphi - Delphi 7 中的 Indy 10.6.2 idFTP - 接收文件时文件名中的 native 符号问题 - 可能是 DefStringEncoding 的不良行为

转载 作者:行者123 更新时间:2023-12-04 14:59:38 27 4
gpt4 key购买 nike

我正在使用 Indy 10.6.2 以及 Delphi 7 和 Windows 10 64 位波兰语。

我运行一个 FTP 服务器:FTPServer: TIdFTPServer,通过使用 FTPClient: TIdFTP,我试图获取一个文件名中包含国家符号的文件。不幸的是,在 OnRetrieveFile 事件的服务器端调用 Get() 函数后,我最终得到的不是 AFileName 中的国家符号问号,这显然会导致其他异常。

对于测试,我同时运行:服务器和客户端在同一台机器和同一应用程序中以消除任何其他干扰。

在客户端,我已经尝试过:

 FTPClient.DefStringEncoding := IndyTextEncoding_UTF8;
FTPClient.IOHandler.DefStringEncoding := IndyTextEncoding_UTF8;
FTPClient.IOHandler.DefAnsiEncoding := IndyTextEncoding_UTF8;

在服务器端,我试过:

 ASender.Connection.IOHandler.DefStringEncoding := IndyTextEncoding_UTF8;
ASender.Connection.IOHandler.DefAnsiEncoding := IndyTextEncoding_UTF8;

它们都没有任何区别。我也尝试了其他编码,但也失败了。

下面是我的测试应用程序,以及客户端和服务器的文本 DFM 值。

unit Glowny_FTP;

interface

uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, StdCtrls, IdBaseComponent, IdComponent, IdTCPConnection,
IdTCPClient, IdExplicitTLSClientServerBase, IdFTP, ZLibCompression, IdGlobal,
IdIOHandler, IdIOHandlerStream, IdIOHandlerSocket, IdIOHandlerStack,
IdCustomTCPServer, IdTCPServer, IdCmdTCPServer, IdFTPServer, IdContext,
IdAntiFreezeBase, IdAntiFreeze;

type
TForm1 = class(TForm)
Button1: TButton;
FTPClient: TIdFTP;
FTPServer: TIdFTPServer;
IdAntiFreeze1: TIdAntiFreeze;
procedure Button1Click(Sender: TObject);
procedure FTPServerUserLogin(ASender: TIdFTPServerContext;
const AUsername, APassword: String; var AAuthenticated: Boolean);
procedure FTPServerRetrieveFile(ASender: TIdFTPServerContext;
const AFileName: WideString; var VStream: TStream);
private
{ Private declarations }
public
{ Public declarations }
end;

var
Form1: TForm1;

implementation

{$R *.dfm}

procedure TForm1.Button1Click(Sender: TObject);

begin

FTPServer.DefaultPort := 1350;
FTPServer.Active := True;

FTPClient.Host := '127.0.0.1';
FTPClient.Port := 1350;
FTPClient.Username := 'new';
FTPClient.Password := 'pass';
FTPClient.Connect;
// FTPClient.DefStringEncoding := IndyTextEncoding_UTF8;
// FTPClient.IOHandler.DefStringEncoding := IndyTextEncoding_UTF8;
// FTPClient.IOHandler.DefAnsiEncoding := IndyTextEncoding_UTF8;

FTPClient.Get('sample ąęśćłó','C:\węzły.cpy',True,False); // <-- filename containing national symbols
FTPClient.Disconnect;
end;

procedure TForm1.FTPServerUserLogin(ASender: TIdFTPServerContext;
const AUsername, APassword: String; var AAuthenticated: Boolean);
begin
if (APassword='pass') then
begin
AAuthenticated := True;
end else AAuthenticated := False;
end;

procedure TForm1.FTPServerRetrieveFile(ASender: TIdFTPServerContext;
const AFileName: WideString; var VStream: TStream);
begin
// ASender.Connection.IOHandler.DefStringEncoding := IndyTextEncoding_UTF8;
// ASender.Connection.IOHandler.DefAnsiEncoding := IndyTextEncoding_UTF8;
VStream := TFileStream.Create(AFileName+'.serv',fmOpenRead); //<-- here I get: "/sample ??????" without UTF8 and "/sample ????" with UTF8
end;

end.

也许还有有趣的 DFM 部分:

object FTPClient: TIdFTP
Passive = True
ConnectTimeout = 0
DataPort = 20
Password = 'TaJnEH1aS2loD3oSt4ePu'
TransferType = ftBinary
NATKeepAlive.UseKeepAlive = False
NATKeepAlive.IdleTimeMS = 0
NATKeepAlive.IntervalMS = 0
ProxySettings.ProxyType = fpcmNone
ProxySettings.Port = 0
Left = 104
Top = 72
end
object FTPServer: TIdFTPServer
Bindings = <>
DefaultPort = 1350
TerminateWaitTime = 100
CommandHandlers = <>
ExceptionReply.Code = '500'
ExceptionReply.Text.Strings = (
'Unknown Internal Error')
Greeting.Code = '220'
Greeting.Text.Strings = (
'Indy FTP Server ready.')
MaxConnectionReply.Code = '300'
MaxConnectionReply.Text.Strings = (
'Too many connections. Try again later.')
ReplyTexts = <>
ReplyUnknownCommand.Code = '500'
ReplyUnknownCommand.Text.Strings = (
'Unknown Command')
PathProcessing = ftppUnix
AnonymousAccounts.Strings = (
'anonymous'
'ftp'
'guest')
OnUserLogin = FTPServerUserLogin
OnRetrieveFile = FTPServerRetrieveFile
SITECommands = <>
MLSDFacts = []
ReplyUnknownSITCommand.Code = '500'
ReplyUnknownSITCommand.Text.Strings = (
'Invalid SITE command.')
Left = 104
Top = 8
end

更新:

经过雷米的帮助还是没有效果。在 FTPClient.Get() 中的 UTF8Encode() 之后,我在服务器端得到了更多问号。现在我的代码如下所示:(我检查 Form1.Caption 上的 AFileName 只是为了快速调试)

...
FTPClient.Connect;

FTPClient.DefStringEncoding := IndyTextEncoding_UTF8;
FTPClient.IOHandler.DefStringEncoding := IndyTextEncoding_UTF8;
FTPClient.IOHandler.DefAnsiEncoding := IndyTextEncoding_8Bit;

FTPClient.Get(UTF8Encode('sample ąęśćłó'),'C:\węzły.cpy',True,False);
FTPClient.Disconnect;

procedure TForm1.FTPServerRetrieveFile(ASender: TIdFTPServerContext;
const AFileName: WideString; var VStream: TStream);
begin
Form1.Caption := AFileName;
VStream := TFileStream.Create('C:\tymczas z węzłami obl.aqr',fmOpenRead);
// VStream := TFileStream.Create(AFileName+'.serv',fmOpenRead);

end;

procedure TForm1.FTPServerConnect(AContext: TIdContext);
begin
AContext.Connection.IOHandler.DefStringEncoding := IndyTextEncoding_UTF8;
AContext.Connection.IOHandler.DefAnsiEncoding := IndyTextEncoding_UTF8;
end;

最佳答案

背景

IOHandler 的 DefStringEncoding需要设置为通过套接字传输字符串时要使用的字节编码。 Indy 将通过使用 DefStringEncoding 将其编码为字节来发送一个 Unicode 字符串。 .相反,Indy 将通过使用 DefStringEncoding 将接收到的字节解码为 Unicode 来读取 Unicode 字符串。 .

在 Delphi 2007 及更早版本中,涉及一个额外的步骤。 IOHandler 的 DefAnsiEncoding需要设置为您要与 AnsiString 一起使用的编码秒。 Indy 将发出 AnsiString首先使用 DefAnsiEncoding 将其从 ANSI 解码为 Unicode然后使用 DefStringEncoding 发送该 Unicode .相反,Indy 将读入 AnsiString。首先使用 DefStringEncoding 读取 Unicode 字符串然后使用 DefAnsiEncoding 将该 Unicode 编码为 ANSI .

默认情况下,DefStringEncodingIndyTextEncoding_ASCII (出于历史原因),以及 DefAnsiEncodingIndyTextEncoding_OSDefault .

在客户端

TIdFTP会自动切换 IOHandler 的 DefStringEncodingIndyTextEncoding_UTF8如果确定服务器支持 UTF-8。如果没有 UTF-8 扩展,FTP 协议(protocol)需要使用 ASCII。所以,你真的不应该弄乱客户的 DefStringEncoding完全没有。

您最初将远程文件名作为波兰语编码的 AnsiString 传递, 所以有 DefAnsiEncoding设置为 IndyTextEncoding_OSDefault在设置为波兰语言环境的操作系统上有意义。与 DefStringEncoding设置为 IndyTextEncoding_UTF8 , 正确的 UTF-8 应该传输到服务器。

您后来更改了客户端以传入 UTF-8 编码的 AnsiString相反,但您设置了 DefAnsiEncodingIndyTextEncoding_8Bit , 所以 AnsiString未正确转换为 Unicode,因此后续转换为 UTF-8 的格式不正确。 DefAnsiEncoding需要设置为IndyTextEncoding_UTF8在那种情况下。我建议离开 DefAnsiEncoding设置为 IndyTextEncoding_OSDefault并使用操作系统语言环境编码 AnsiString s,除非您有令人信服的理由不这样做。

在服务器端

TIdFTPServer将自动切换 IOHandler 的 DefStringEncodingIndyTextEncoding_UTF8仅当客户端发出 OPTS UTF-8 <NLST>OPTS UTF8 ON命令(TIdFTP 执行,但其他客户端可能不执行)。请注意,这不符合 RFC 2640 ,Indy 尚未完全实现。

您同时设置了 DefStringEncodingDefAnsiEncodingIndyTextEncoding_UTF8 ,这在大多数情况下通常是可以的。你最终会得到 UTF-8 编码的 AnsiString s 在公开访问 AnsiString 的事件中

然而,OnRetrieveFile事件使用 WideString为其AFileName您的 Delphi 版本中的参数。服务器从套接字中以 AnsiString 形式读入文件名后,它被按原样传递给事件处理程序,将其转换为 WideString使用 RTL 自己的转换逻辑,默认情况下使用操作系统的语言环境。所以,在你的情况下,AnsiString是 UTF-8 编码但操作系统语言环境是波兰语,您最终会得到格式错误的转换。幸运的是,您可以通过调用 RTL 的 SetMultiByteConversionCodePage() 来缓解这个问题。预先执行的功能 AnsiString <-> WideString改为使用代码页 65001 (UTF-8) 进行转换。

然而,有几个 TIdFTPServer的事件使用 WideString Delphi 2007 及更早版本中的参数,因此有相当多的区域 TIdFTPServer的内部结构依赖于 RTL 的默认值 AnsiString <-> WideString转换。所以,我建议离开 DefAnsiEncoding设置为 IndyTextEncoding_OSDefault在服务器端也是如此,除非您有令人信服的理由不这样做。如果要强制DefStringEncodingIndyTextEncoding_UTF8不管OPTS命令,这没问题(前提是您的客户端只发送 UTF-8 编码路径)。


话虽如此,试试这个:

unit Glowny_FTP;

interface

uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, StdCtrls, IdBaseComponent, IdComponent, IdTCPConnection,
IdTCPClient, IdExplicitTLSClientServerBase, IdFTP, ZLibCompression, IdGlobal,
IdIOHandler, IdIOHandlerStream, IdIOHandlerSocket, IdIOHandlerStack,
IdCustomTCPServer, IdTCPServer, IdCmdTCPServer, IdFTPServer, IdContext,
IdAntiFreezeBase, IdAntiFreeze;

type
TForm1 = class(TForm)
Button1: TButton;
FTPClient: TIdFTP;
FTPServer: TIdFTPServer;
IdAntiFreeze1: TIdAntiFreeze;
procedure Button1Click(Sender: TObject);
procedure FTPServerConnect(ASender: TIdContext);
procedure FTPServerUserLogin(ASender: TIdFTPServerContext;
const AUsername, APassword: String; var AAuthenticated: Boolean);
procedure FTPServerRetrieveFile(ASender: TIdFTPServerContext;
const AFileName: WideString; var VStream: TStream);
private
{ Private declarations }
public
{ Public declarations }
end;

var
Form1: TForm1;

implementation

{$R *.dfm}

procedure TForm1.Button1Click(Sender: TObject);
begin
FTPServer.DefaultPort := 1350;
FTPServer.Active := True;

FTPClient.Host := '127.0.0.1';
FTPClient.Port := 1350;
FTPClient.Username := 'new';
FTPClient.Password := 'pass';
FTPClient.Connect;
try
// FTPClient.IOHandler.DefStringEncoding := IndyTextEncoding_UTF8;
// FTPClient.IOHandler.DefAnsiEncoding := IndyTextEncoding_OSDefault;
FTPClient.Get('sample ąęśćłó', 'C:\węzły.cpy', True, False);

{ or:
//FTPClient.IOHandler.DefStringEncoding := IndyTextEncoding_UTF8;
FTPClient.IOHandler.DefAnsiEncoding := IndyTextEncoding_UTF8;
FTPClient.Get(UTF8Encode('sample ąęśćłó'), 'C:\węzły.cpy', True, False);
}
finally
FTPClient.Disconnect;
end;
end;

procedure TForm1.FTPServerConnect(ASender: TIdContext);
begin
ASender.Connection.IOHandler.DefStringEncoding := IndyTextEncoding_UTF8;
//ASender.Connection.IOHandler.DefAnsiEncoding := IndyTextEncoding_OSDefault;
end;

procedure TForm1.FTPServerUserLogin(ASender: TIdFTPServerContext;
const AUsername, APassword: String; var AAuthenticated: Boolean);
begin
AAuthenticated := (AUsername = 'new') and (APassword = 'pass');
end;

procedure TForm1.FTPServerRetrieveFile(ASender: TIdFTPServerContext;
const AFileName: WideString; var VStream: TStream);
begin
VStream := TFileStream.Create(AFileName + '.serv', fmOpenRead);
end;

end.

或者这个:

unit Glowny_FTP;

interface

uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, StdCtrls, IdBaseComponent, IdComponent, IdTCPConnection,
IdTCPClient, IdExplicitTLSClientServerBase, IdFTP, ZLibCompression, IdGlobal,
IdIOHandler, IdIOHandlerStream, IdIOHandlerSocket, IdIOHandlerStack,
IdCustomTCPServer, IdTCPServer, IdCmdTCPServer, IdFTPServer, IdContext,
IdAntiFreezeBase, IdAntiFreeze;

type
TForm1 = class(TForm)
Button1: TButton;
FTPClient: TIdFTP;
FTPServer: TIdFTPServer;
IdAntiFreeze1: TIdAntiFreeze;
procedure FormCreate(Sender: TObject);
procedure Button1Click(Sender: TObject);
procedure FTPServerConnect(ASender: TIdContext);
procedure FTPServerUserLogin(ASender: TIdFTPServerContext;
const AUsername, APassword: String; var AAuthenticated: Boolean);
procedure FTPServerRetrieveFile(ASender: TIdFTPServerContext;
const AFileName: WideString; var VStream: TStream);
private
{ Private declarations }
public
{ Public declarations }
end;

var
Form1: TForm1;

implementation

{$R *.dfm}

procedure TForm1.FormCreate(Sender: TObject);
begin
SetMultiByteConversionCodePage(CP_UTF8);
end;

procedure TForm1.Button1Click(Sender: TObject);
begin
FTPServer.DefaultPort := 1350;
FTPServer.Active := True;

FTPClient.Host := '127.0.0.1';
FTPClient.Port := 1350;
FTPClient.Username := 'new';
FTPClient.Password := 'pass';
FTPClient.Connect;
try
// FTPClient.IOHandler.DefStringEncoding := IndyTextEncoding_UTF8;
// FTPClient.IOHandler.DefAnsiEncoding := IndyTextEncoding_OSDefault;
FTPClient.Get('sample ąęśćłó', 'C:\węzły.cpy', True, False);

{ or:
//FTPClient.IOHandler.DefStringEncoding := IndyTextEncoding_UTF8;
FTPClient.IOHandler.DefAnsiEncoding := IndyTextEncoding_UTF8;
FTPClient.Get(UTF8Encode('sample ąęśćłó'), 'C:\węzły.cpy', True, False);
}
finally
FTPClient.Disconnect;
end;
end;

procedure TForm1.FTPServerConnect(ASender: TIdContext);
begin
ASender.Connection.IOHandler.DefStringEncoding := IndyTextEncoding_UTF8;
ASender.Connection.IOHandler.DefAnsiEncoding := IndyTextEncoding_UTF8;
end;

procedure TForm1.FTPServerUserLogin(ASender: TIdFTPServerContext;
const AUsername, APassword: String; var AAuthenticated: Boolean);
begin
AAuthenticated := (AUsername = 'new') and (APassword = 'pass');
end;

procedure TForm1.FTPServerRetrieveFile(ASender: TIdFTPServerContext;
const AFileName: WideString; var VStream: TStream);
begin
VStream := TFileStream.Create(AFileName + '.serv', fmOpenRead);
end;

end.

关于delphi - Delphi 7 中的 Indy 10.6.2 idFTP - 接收文件时文件名中的 native 符号问题 - 可能是 DefStringEncoding 的不良行为,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/67231458/

27 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com