2012-06-18 10 views
5

Estoy tratando de transferir un archivo a través de un IHttpHandler, el código es bastante simple. Sin embargo, cuando comienzo una sola transferencia, usa aproximadamente el 20% de la CPU. Si tuviera que escalar esto a 20 transferencias simultáneas, la CPU es muy alta. ¿Hay alguna manera mejor de que pueda hacer esto para mantener baja la CPU? el código del cliente solo envía fragmentos del archivo de 64 KB a la vez.Transferencia de archivos comiendo una gran cantidad de CPU

public void ProcessRequest(HttpContext context) 
{ 
     if (context.Request.Params["secretKey"] == null) 
     { 

     } 
     else 
     { 
      accessCode = context.Request.Params["secretKey"].ToString(); 
     } 

     if (accessCode == "test") 
     { 
      string fileName = context.Request.Params["fileName"].ToString(); 
      byte[] buffer = Convert.FromBase64String(context.Request.Form["data"]); 
      string fileGuid = context.Request.Params["smGuid"].ToString(); 
      string user = context.Request.Params["user"].ToString(); 

      SaveFile(fileName, buffer, user); 
     } 
} 

public void SaveFile(string fileName, byte[] buffer, string user) 
{ 
     string DirPath = @"E:\Filestorage\" + user + @"\"; 

     if (!Directory.Exists(DirPath)) 
     { 
      Directory.CreateDirectory(DirPath); 
     } 

     string FilePath = @"E:\Filestorage\" + user + @"\" + fileName; 
     FileStream writer = new FileStream(FilePath, File.Exists(FilePath) ? FileMode.Append : FileMode.Create, FileAccess.Write, FileShare.ReadWrite); 
     writer.Write(buffer, 0, buffer.Length); 
     writer.Close(); 
} 

Aquí está mi código de cliente:

//Set filename from object 
       string FileName; 
       FileName = System.IO.Path.GetFileName(pubAttFullPath.ToString()); 

       //Open file 
       string file = System.IO.Path.GetFileName(pubAttFullPath.ToString()); 
       FileStream fileStream = new FileStream(file, FileMode.Open, FileAccess.Read); 
       //Chunk size that will be sent to Server 
       int chunkSize = 65536; 
       // Unique file name 
       string fileName = smGuid.ToString() + "_" + FileName; 
       int totalChunks = (int)Math.Ceiling((double)fileStream.Length/chunkSize); 
       // Loop through the whole stream and send it chunk by chunk; 
       for (int i = 0; i < totalChunks; i++) 
       { 
        bool doRecieve = true; 
        int cpt = 0; 
        do 
        { 
         int startIndex = i * chunkSize; 
         int endIndex = (int)(startIndex + chunkSize > fileStream.Length ? fileStream.Length : startIndex + chunkSize); 
         int length = endIndex - startIndex; 

         byte[] bytes = new byte[length]; 
         fileStream.Read(bytes, 0, bytes.Length); 


         //Request url, Method=post Length and data. 
         string requestURL = "http://localhost:16935/Transfer.doit"; 
         HttpWebRequest request = (HttpWebRequest)WebRequest.Create(requestURL); 
         // Wait 5 min for answer before close connection. 
         request.Timeout = 300000; 
         request.Method = "POST"; 
         request.ContentType = "application/x-www-form-urlencoded"; 

         // Chunk(buffer) is converted to Base64 string that will be convert to Bytes on the handler. 
         string requestParameters = @"fileName=" + fileName + @"&secretKey=test" + @"&currentChunk=" + i + @"&totalChunks=" + totalChunks + @"&smGuid=" + smGuid 
         + "&user=" + userSID.ToString() + 
         "&data=" + HttpUtility.UrlEncode(Convert.ToBase64String(bytes)); 

         // finally whole request will be converted to bytes that will be transferred to HttpHandler 
         byte[] byteData = Encoding.UTF8.GetBytes(requestParameters); 

         request.ContentLength = byteData.Length; 
         try 
         { 
          Stream writer = request.GetRequestStream(); 
          writer.Write(byteData, 0, byteData.Length); 
          writer.Close(); 
          // here we will receive the response from HttpHandler 
          StreamReader stIn = new StreamReader(request.GetResponse().GetResponseStream()); 
          string strResponse = stIn.ReadToEnd(); 
          stIn.Close(); 
          doRecieve = true; 
         } 
         catch (WebException webException) 
         { 
          if (webException.Status == WebExceptionStatus.ConnectFailure || 
           webException.Status == WebExceptionStatus.ConnectionClosed || 
           webException.Status == WebExceptionStatus.ReceiveFailure || 
           webException.Status == WebExceptionStatus.SendFailure || 
           webException.Status == WebExceptionStatus.Timeout) 
          { 
           Thread.Sleep(5000); 
           doRecieve = false; 
           cpt++; 
          } 
          else { 
           // if the exception is not those ones then get out 
           doRecieve = true; 
          } 
         } 
         catch (Exception e) 
         { 
          doRecieve = true; 
         } 
        } 
        // will try to send 3 times the current chunk before quitting 
        // can't try it so try it and give me the feedback 
        while(doRecieve == false && cpt < 3); 
       } 
+0

¿En qué basa sus estadísticas de uso de CPU? – CodingGorilla

+0

Iniciando una transferencia y viendo perfmon. Yo soy el único que lo usa. –

+0

¿Está esto en una máquina de desarrollo o en un servidor real? – CodingGorilla

Respuesta

1

No he probado esta teoría, pero trabajar con FromBase64String puede ser la causa. Encontré this case donde alguien se estaba quedando sin memoria usando este método.

En su lugar, puede probar FromBase64Transform, que está diseñado para manejar una secuencia de datos.


O si no es necesario usar base 64, por cualquier motivo, echa un vistazo a this solution from Scott Hanselman.

+0

Eso es interesante, voy a dar una oportunidad. Sin embargo, la pregunta es que no soy un experto en base64 ni nada, pero dado que mi transporte es SSL, ¿necesito codificar la matriz de bytes en base64? –

+0

No sabía por qué estabas usando base64, en realidad. Pero para responder a su pregunta, no, no necesitará hacer una decodificación base64 para un archivo transferido a través de SSL. Mira esto para encontrar una solución (suponiendo que estás usando multipart/form-data) ... http://www.hanselman.com/blog/ABackToBasicsCaseStudyImplementingHTTPFileUploadWithASPNETMVCIncludingTestsAndMocks.aspx –

+0

Intenté usar este método para convertir la matriz de bytes en una cadena (http://stackoverflow.com/questions/472906/net-string-to-byte-array-c-sharp) y decodificando en una matriz de bytes en el controlador http y aún usaba 15% de CPU (y corrompió el archivo) ...No estoy seguro de cómo más puedo transportar la matriz de bytes en los parámetros. –

Cuestiones relacionadas