Note
Access to this page requires authorization. You can try signing in or changing directories.
Access to this page requires authorization. You can try changing directories.
Recuerdo un caso en el que un cliente tenía un OutOfMemory subiendo ficheros de más de 1GB a un FTP usando las clases de .NET. Recordemos que el límite de memoria virtual que tiene un proceso de 64 bit es de 8 TB o 128 TB en Windows 8.1/10/2012 R2 (Memory Limits for Windows and Windows Server Releases)
Técnicamente es posible tener ese OutOfMemory puesto que podríamos crear una aplicación que consuma memoria hasta llegar a ese límite de memoria. Pero, ¿cómo es posible tener ese OutOfMemory si subimos un fichero de 1.4 GB y nos quedan más de 120 TB libres en el proceso?
Usando este código (de la MSDN) , veremos cómo es posible conseguir la excepción subiendo el fichero:
string user = "";
string pass = "";
string file = "";
string ftpUri = "";
try
{
if (args.Length == 0)
{
Console.WriteLine("Enter username: ");
user = Console.ReadLine();
Console.WriteLine("Enter password: ");
pass = Console.ReadLine();
Console.WriteLine("Enter file name: ");
file = Console.ReadLine();
Console.WriteLine("Enter FTP URi: ");
ftpUri = Console.ReadLine();
}
else
{
user = args[0];
pass = args[1];
file = args[2];
ftpUri = args[3];
}
FtpWebRequest request = (FtpWebRequest)WebRequest.Create(ftpUri);
request.Method = WebRequestMethods.Ftp.UploadFile;
request.Credentials = newNetworkCredential(user, pass);
StreamReader sourceStream = newStreamReader(file);
Console.ReadLine();
byte[] fileContents = Encoding.UTF8.GetBytes(sourceStream.ReadToEnd());
sourceStream.Close();
request.ContentLength = fileContents.Length;
Stream requestStream = request.GetRequestStream();
requestStream.Write(fileContents, 0, fileContents.Length);
requestStream.Close();
FtpWebResponse response = (FtpWebResponse)request.GetResponse();
Console.WriteLine("Upload File Complete, status {0}", response.StatusDescription);
response.Close();
}
catch(Exception ex)
{
Console.WriteLine("An exception has occurred: {0}", ex.Message);
}
Como decía, ejecutando este código, tendremos una OutOfMemoryException, concretamente cuando intentemos convertir el Stream del fichero que queremos subir a un array de bytes:
byte[] fileContents = Encoding.UTF8.GetBytes(sourceStream.ReadToEnd());
Si hemos adjuntado un depurador, veremos algo como esto:
# Call Site
00 KERNELBASE!RaiseException
01 clr!DllCanUnloadNowInternal
02 clr!TranslateSecurityAttributes
03 clr!NGenCreateNGenWorker
04 mscorlib_ni!System.Text.StringBuilder.ToString()
05 FTPClient!FTPCLient.Main(System.String[])
Lo que ocurre es que estamos convirtiendo un fichero muy grande en el array de bytes directamente. Lo recomendado sería usar un buffer para evitar este comportamiento: Subir ficheros con buffer
También podemos usar los objetos muy grandes de .NET (como menciono en este otro artículo: OutOfMemoryException al manejar StringBuilder en un proceso de 64 bitshttps://blogs.msdn.com/b/desarrolloweb/archive/2016/02/17/stringbuilder-outofmemory.aspx)
Espero que os sirva.
-- José Ortega Gutiérrez