Saltar al contenido

System.IO.IOException: “El archivo existe” al usar System.IO.Path.GetTempFileName() – ¿resoluciones?

Al fin luego de mucho luchar pudimos dar con la contestación de este asunto que algunos de nuestros usuarios de esta web han tenido. Si tienes algún detalle que aportar puedes compartir tu conocimiento.

Solución:

Si esto te está pasando en un entorno de producción o con una aplicación que no puede cambiar, la solución rápida es vaciar la carpeta Temp.

Dependiendo del usuario que esté ejecutando la aplicación, debe

  • Vacío C:WindowsTemp (para IIS o servicios que se ejecutan bajo LocalSystem cuenta)
  • O %temp% para usuarios registrados localmente (que para mí es C:UsersMyUserNameAppDataLocalTemp).

Por otro lado, si su propio código arroja esto y desea evitar que esto vuelva a suceder:

  1. ¡No utilice System.IO.Path.GetTempFileName()!

GetTempFileName() es un contenedor de Win32 Api de dos décadas de antigüedad. Genera nombres de archivo que colisionarán muy fácilmente. Elude esas colisiones al realizar un bucle intensivo en el sistema de archivos, iterando los posibles nombres de archivo de "%temp%tmp0000.tmp" para "tmpFFFF.tmp" y saltándose los ya existentes. Este es un algoritmo intensivo de E/S, lento y francamente terrible. También usar solo 4 caracteres hexadecimales es lo que hace que el límite artificial de 65536 archivos antes de fallar.

La alternativa es generar nombres de archivos que no entren en conflicto. Por ejemplo, reutilicemos GUID's Lógica: 32 dígitos hexadecimales casi nunca colisionarán.

private string GetTempFileName()

    return Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString());

// Sample: c:WindowsTemp2e38fe87-f6bb-4b0d-90b3-2d07016324c1

Esto amplía el límite de 65k a 4k millones de archivos como máximo (teóricamente)… Por supuesto, haber filtrado 65k archivos ya es terrible, así que…

  1. ¡No filtre archivos temporales!

Verifique dos veces su aplicación para ver todos los caminos felices e infelices (como excepciones inesperadas). Asegúrese de que esté desechando correctamente cada FileStream y eliminando los archivos temporales en los bloques Finalmente.

  1. Limpiar la carpeta temporal

Límpielo ahora y eduque al administrador del sistema para que lo limpie periódicamente, porque no puede confiar en todas las aplicaciones en la naturaleza. En mis propios servidores, automatizaría esta tarea usando:

  • Para WindowsTemp global

schtasks /Create /TR "cmd /c call DEL /F /S /Q %^TEMP%" /TN "Delete Global Temp Files" /sc WEEKLY /ST 12:00 /ru system

  • Para el usuario actual:

schtasks /Create /TR "cmd /c call DEL /F /S /Q %^TEMP%" /TN "Delete %username% Temp Files" /sc WEEKLY /ST 12:00

Como mencioné en mi último comentario, creo que la única forma segura de hacerlo es preguntarle al usuario si quiere que elimine los archivos e intentarlo de nuevo. Está imperativo que obtenga la entrada de los usuarios en esto, de esta manera es bajo su propio riesgo. En mi cabeza es algo similar a.

public Stream GetStream(Stream cursorStream)

    try
    
       //getting stream
    
    catch(IOE)
    
        MessageBox.Show(this, "Unable to get stream, your temporary
                              folder may be full, do you want to try deleting 
                                some and try again?");
         if(yes)
         try
         
             //delete and try again
             return GetStream(cursorStream);
         
         catch(IOE)
          
                //no luck
           
          else
              return null;
    


Una verificación opcional para asegurarse podría ser,

Directory.EnumerateFiles(Path.GetTempPath(), "*", SearchOption.TopLevelOnly)
  .Count() == ushort.MaxValue;

Aquí está el código que usé al final, y lo puse al principio de la ruta del código de inicialización de mi aplicación, antes de cualquier llamada a Cursor.LoadFromStream puede ocurrir:

    private void WarnUserIfTempFolderFull()
    
        string tempFile = null;
        try
        
            tempFile = Path.GetTempFileName();
        
        catch (IOException e)
        
            string problem = "The Temporary Folder is full.";

            string message = "ProductName has detected that the Windows Temporary Folder is full. n" + 
                             "This may prevent the ProductName from functioning correctly.n" + 
                             "Please delete old files in your temporary folder (%TEMP%) and try again.";

            Logger.Warn(problem);

            MessageBox.Show(message, caption: problem);
        
        finally
        
            if (tempFile != null) File.Delete(tempFile);
        
    

Sección de Reseñas y Valoraciones

Nos puedes añadir valor a nuestro contenido informacional aportando tu veteranía en las explicaciones.

¡Haz clic para puntuar esta entrada!
(Votos: 0 Promedio: 0)


Tags : / /

Utiliza Nuestro Buscador

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *