我的Delphi 2010应用程序使用多线程上传内容,上传的数据被发布到需要登录的PHP / Web应用程序,所以我需要使用共享/全局cookie管理器(我正在使用 Indy10 Revision 4743 )因为TIdCookieManager不是线程安全的:(
此外,服务器端会话ID每5分钟自动重新生成一次,因此我必须保持全局&本地cookie经理同步。
我的代码如下所示:
TUploadThread = class(TThread)
// ...
var
GlobalCookieManager : TIdCookieManager;
procedure TUploadThread.Upload(FileName : String);
var
IdHTTP : TIdHTTP;
TheSSL : TIdSSLIOHandlerSocketOpenSSL;
TheCompressor : TIdCompressorZLib;
TheCookieManager : TIdCookieManager;
AStream : TIdMultipartFormDataStream;
begin
ACookieManager := TIdCookieManager.Create(IdHTTP);
// Automatically sync cookies between local & global Cookie managers
@TheCookieManager.OnNewCookie := pPointer(Cardinal(pPointer( procedure(ASender : TObject; ACookie : TIdCookie; var VAccept : Boolean)
begin
OmniLock.Acquire;
try
GlobalCookieManager.CookieCollection.AddCookie(ACookie, TIdHTTP(TIdCookieManager(ASender).Owner).URL{IdHTTP.URL});
finally
OmniLock.Release;
end; // try/finally
VAccept := True;
end )^ ) + $0C)^;
// ======================================== //
IdHTTP := TIdHTTP.Create(nil);
with IdHTTP do
begin
HTTPOptions := [hoForceEncodeParams, hoNoParseMetaHTTPEquiv];
AllowCookies := True;
HandleRedirects := True;
ProtocolVersion := pv1_1;
IOHandler := TheSSL;
Compressor := TheCompressor;
CookieManager := TheCookieManager;
end; // with
OmniLock.Acquire;
try
// Load login info/cookies
TheCookieManager.CookieCollection.AddCookies(GlobalCookieManager.CookieCollection);
finally
OmniLock.Release;
end; // try/finally
AStream := TIdMultipartFormDataStream.Create;
with Stream.AddFile('file_name', FileName, 'application/octet-stream') do
begin
HeaderCharset := 'utf-8';
HeaderEncoding := '8';
end; // with
IdHTTP.Post('https://www.domain.com/post.php', AStream);
AStream.Free;
end;
但它不起作用!我在调用AddCookies()
时遇到此异常Project MyEXE.exe引发了带有消息的异常类EAccessViolation '地址00000000的访问冲突。读取地址00000000'。
我也尝试过使用assign(),即
TheCookieManager.CookieCollection.Assign(GlobalCookieManager.CookieCollection);
但我仍然得到同样的例外,通常在这里:
TIdCookieManager.GenerateClientCookies()
任何人都知道如何解决这个问题?
答案 0 :(得分:5)
请勿对OnNewCookie
事件使用匿名过程。改为使用普通的类方法:
procedure TUploadThread.NewCookie(ASender: TObject; ACookie : TIdCookie; var VAccept : Boolean);
var
LCookie: TIdCookie;
begin
LCookie := TIdCookieClass(ACookie.ClassType).Create;
LCookie.Assign(ACookie);
OmniLock.Acquire;
try
GlobalCookieManager.CookieCollection.AddCookie(LCookie, TIdHTTP(TIdCookieManager(ASender).Owner).URL);
finally
OmniLock.Release;
end;
VAccept := True;
end;
或者:
procedure TUploadThread.NewCookie(ASender: TObject; ACookie : TIdCookie; var VAccept : Boolean);
begin
OmniLock.Acquire;
try
GlobalCookieManager.CookieCollection.AddServerCookie(ACookie.ServerCookie, TIdHTTP(TIdCookieManager(ASender).Owner).URL);
finally
OmniLock.Release;
end;
VAccept := True;
end;
然后像这样使用它:
procedure TUploadThread.Upload(FileName : String);
var
IdHTTP : TIdHTTP;
TheSSL : TIdSSLIOHandlerSocketOpenSSL;
TheCompressor : TIdCompressorZLib;
TheCookieManager : TIdCookieManager;
TheStream : TIdMultipartFormDataStream;
begin
IdHTTP := TIdHTTP.Create(nil);
try
...
TheCookieManager := TIdCookieManager.Create(IdHTTP);
TheCookieManager.OnNewCookie := NewCookie;
with IdHTTP do
begin
HTTPOptions := [hoForceEncodeParams, hoNoParseMetaHTTPEquiv];
AllowCookies := True;
HandleRedirects := True;
ProtocolVersion := pv1_1;
IOHandler := TheSSL;
Compressor := TheCompressor;
CookieManager := TheCookieManager;
end; // with
OmniLock.Acquire;
try
// Load login info/cookies
TheCookieManager.CookieCollection.AddCookies(GlobalCookieManager.CookieCollection);
finally
OmniLock.Release;
end;
TheStream := TIdMultipartFormDataStream.Create;
try
with TheStream.AddFile('file_name', FileName, 'application/octet-stream') do
begin
HeaderCharset := 'utf-8';
HeaderEncoding := '8';
end;
IdHTTP.Post('https://www.domain.com/post.php', TheStream);
finally
TheStream.Free;
end;
finally
IdHTTP.Free;
end;
end;
答案 1 :(得分:3)
如果我不得不猜测,我会说你的问题就在这里:
// Automatically sync cookies between local & global Cookie managers
@TheCookieManager.OnNewCookie := pPointer(Cardinal(pPointer( procedure(ASender : TObject; ACookie : TIdCookie; var VAccept : Boolean)
begin
OmniLock.Acquire;
try
GlobalCookieManager.CookieCollection.AddCookie(ACookie, TIdHTTP(TIdCookieManager(ASender).Owner).URL{IdHTTP.URL});
finally
OmniLock.Release;
end; // try/finally
VAccept := True;
end )^ ) + $0C)^;
我不确定$0C
幻数是什么,但我打赌所有这些演员都在那里,因为你有时间让编译器接受这个。它给了你类型错误,说你无法将一件事分配给另一件事。
那些类型错误是有原因的!如果你在类型系统中乱哄哄的话,很可能会破坏。尝试将该匿名方法转换为TUploadThread上的常规方法并以此方式分配,并查看它是否无法正常工作。
答案 2 :(得分:2)
回应评论:
谢谢你们,我转换成了正常的方法,但我还是得到了 AddCookies()中的异常,最后一个发生在读取的行中 FRWLock.BeginWrite;在这个程序中 TIdCookies.LockCookieList(AAccessType:TIdCookieAccess): TIdCookieList;
如果您的错误是Read of address 00000000
的访问冲突,则具有非常具体的含义。这意味着您正在尝试使用 nil 的对象执行某些操作。
当你得到它时,打破调试器。如果错误发生在您说它正在发生的行上,那么此时几乎可以确定Self
或FRWLock
nil 。检查两个变量并找出尚未构建的变量,并指出解决方案。