问题描述
我正在使用 TIdFTP
(Indy 10.6) 上传超过 1GB 的大文件。
这是我的代码:
var
FTPfile: TMemoryStream;
begin
...
FTPFile := TMemoryStream.Create;
...
TRY
IdFTP1.Get ('Myfile.pdf',FTPfile,false);
FTPfile.Position := 0;
EXCEPT
On E: Exception do
ShowMessage (E.Message);
END;
我使用的是 32 位版本的 Windows。
解决方法
您正在尝试将 1GB 文件下载到 TMemoryStream
。除了一般来说只是一个坏主意之外,这样做的主要问题是 FTP 协议不报告正在传输的文件的大小,因此 TIdFTP.Get()
无法预先分配 {{1 }} 的内部缓冲区。因此,随着文件下载,TMemoryStream
将不得不增长很多次,每次它都会分配一个全新的缓冲区,将现有数据复制到其中,并释放旧缓冲区。这意味着在增长操作期间,内存中存在 2 个缓冲区的时间窗口很小。因此,您实际上使用的内存比您想象的要多得多。在内存中存储 1GB 的文件确实突破了 32 位进程甚至可以分配多少内存的限制。
最终,TMemoryStream.Capacity
会变得如此之大,以至于系统无法再分配第二个缓冲区,从而因“内存不足”错误而失败。
您可以尝试使用 TMemoryStream
或 TIdFTP.Size()
提前确定远程文件的大小,相应地设置 TIdFTP.List()
,然后调用 TMemoryStream.Capacity
。这样,只执行了 1 次内存分配。
此外,请确保在使用完 TIdFTP.Get()
后 Free()
,否则您将泄漏它能够分配的任何内存。
但实际上,您应该将文件下载到磁盘。您可以为此使用 TMemoryStream
,但 TFileStream
有一个重载,它将本地文件路径而不是 TIdFTP.Get()
作为目标。
如果您需要访问内存中的文件数据,您可以在下载完成后根据需要对其进行读取/内存映射。