2008-08-19 12 views
28

Estoy trabajando en un programa que necesita crear varias carpetas temporales para la aplicación. Estos no serán vistos por el usuario. La aplicación está escrita en VB.net. Puedo pensar en algunas maneras de hacerlo, como el nombre de la carpeta incremental o los nombres de las carpetas con números al azar, pero me preguntaba cómo otras personas resuelven este problema.Creación de carpetas temporales

Respuesta

26

Actualización: Agregado File.Exists comprobar por comentario (2012-Jun-19)

Esto es lo que he usado en VB.NET. Esencialmente el mismo que se presentó, excepto que generalmente no quería crear la carpeta de inmediato.

La ventaja de usar GetRandomFilename es que no crea un archivo, por lo que no tiene que limpiar si usa el nombre para algo que no sea un archivo. Me gusta usarlo para el nombre de la carpeta.

Private Function GetTempFolder() As String 
    Dim folder As String = Path.Combine(Path.GetTempPath, Path.GetRandomFileName) 
    Do While Directory.Exists(folder) or File.Exists(folder) 
     folder = Path.Combine(Path.GetTempPath, Path.GetRandomFileName) 
    Loop 

    Return folder 
End Function 

aleatoria Nombre Ejemplo:

C: \ Documents and Settings \ nombre de usuario \ Configuración local \ Temp \ u3z5e0co.tvq


Aquí hay una variación utilizando un GUID para obtener el nombre de la carpeta temporal.

Private Function GetTempFolderGuid() As String 
    Dim folder As String = Path.Combine(Path.GetTempPath, Guid.NewGuid.ToString) 
    Do While Directory.Exists(folder) or File.Exists(folder) 
     folder = Path.Combine(Path.GetTempPath, Guid.NewGuid.ToString) 
    Loop 

    Return folder 
End Function 

GUID Ejemplo:

C: \ Documents and Settings \ nombre de usuario \ Configuración local \ Temp \ 2dbc6db7-2d45-4b75-B27F-0bd492c60496

+0

¿No debería su condición de bucle incluir 'File.Exists' además de' Directory.Exists'? Podría haber un temp_file_ existente en 'Path.GetTempPath', y eso impediría la creación de un directorio. –

+0

Tienes razón. Actualizaré la respuesta para incluir una comprobación de File.Exists() – Rick

+0

Debería ** crear ** la carpeta dentro de la función, por la misma razón que GetTempFileName crea el archivo: para asegurarse de que no hay posibilidad de que alguien más lo haga. – Jaykul

1

Siempre que el nombre de la carpeta no tenga que ser significativo, ¿qué le parece usar un GUID para ellos?

2

Puede generar un GUID para sus nombres de carpetas temporales.

20

Usted tiene que usar System.IO.Path.GetTempFileName()

crea un archivo temporal con un nombre único, de cero bytes en el disco y devuelve la ruta completa de ese archivo.

Puede utilizar System.IO.Path.GetDirectoryName(System.IO.Path.GetTempFileName()) para obtener sólo la información de la carpeta temporal, y crear las carpetas de ahí

Se crean en la carpeta Temp de Windows y eso es considerar una buena práctica

+1

Haga ** NO ** haga esto. Llamar a 'System.IO.Path.GetDirectoryName' en' System.IO.Path.GetTempFileName' es el peor reemplazo para 'System.IO.Path.GetTempPath', ya que crea un archivo que se queda atrás. – Jaykul

3

Algo así como .. .

using System.IO; 

string path = Path.GetTempPath() + Path.GetRandomFileName(); 
while (Directory.Exists(path)) 
path = Path.GetTempPath() + Path.GetRandomFileName(); 

Directory.CreateDirectory(path); 
+0

quizás con un try-catch para volver a ingresar al bucle si no puede crear el directorio ;-) – Jaykul

1

respuestas combinadas de @ adam-Wright y pix0r trabajarán el mejor en mi humilde opinión:


using System.IO; 

string path = Path.GetTempPath() + Path.GetRandomFileName(); 

while (Directory.Exists(path)) 
    path = Path.GetTempPath() + Path.GetRandomFileName(); 

File.Delete(path); 
Directory.CreateDirectory(path); 
0

La ventaja de utilizar System.IO.Path.GetTempFileName es que será un archivo en la ruta local del usuario (es decir, no itinerante). Aquí es exactamente donde lo desea para permisos y razones de seguridad.

8

Solo para aclarar:

System.IO.Path.GetTempPath() 

devuelve solo la ruta de la carpeta a la carpeta temporal.

System.IO.Path.GetTempFileName() 

devuelve el nombre completo del archivo (incluida la ruta) por lo siguiente:

System.IO.Path.Combine(System.IO.Path.GetTempPath(), System.IO.Path.GetTempFileName()) 

es redundante.

+6

GetTempPath y GetTempFileName son 2 métodos completamente diferentes. GetTempPath simplemente devuelve la ubicación de la carpeta temporal del sistema, mientras que GetTempFileName crea un archivo temporal de longitud cero y devuelve la ruta completa de ese archivo. –

8

Hay una posible condición de carrera cuando:

  • la creación de un archivo temporal con GetTempFileName(), eliminarlo, y hacer una carpeta con el mismo nombre, o
  • usando GetRandomFileName() o Guid.NewGuid.ToString para nombrar una carpeta y crear la carpeta tarde

con GetTempFileName() se produce después de la eliminación, otra aplicación podría crear con éxito un archivo temporal con el mismo nombre. El CreateDirectory() fallaría.

Del mismo modo, entre llamar al GetRandomFileName() y crear el directorio, otro proceso podría crear un archivo o directorio con el mismo nombre, lo que también daría como resultado CreateDirectory().

Para la mayoría de las aplicaciones, está bien que un directorio temporal falle debido a una condición de carrera. Es extremadamente raro después de todo. Para ellos, estas carreras a menudo pueden ser ignoradas.

En el mundo del script de shell de Unix, la creación de archivos y directorios temporales de forma segura y sin carreras es un gran problema. Muchas máquinas tienen usuarios múltiples (hostiles), piense en un host web compartido, y muchos scripts y aplicaciones necesitan crear de forma segura archivos temporales y directorios en el directorio compartido/tmp. Vea Safely Creating Temporary Files in Shell Scripts para una discusión sobre cómo crear de forma segura directorios temporales desde scripts de shell.

6

Como @JonathanWright pointed out, existen condiciones de carrera para las soluciones:

  • crear un archivo temporal con GetTempFileName(), eliminarlo, y crear una carpeta con el mismo nombre
  • GetRandomFileName() uso o Guid.NewGuid.ToString para crear una nombre de carpeta al azar, verifique si existe y créelo si no.

Es posible, sin embargo, para crear un directorio temporal único atómicamente mediante la utilización de la API Transactional NTFS (TxF).

TxF tiene una función CreateDirectoryTransacted() que se puede invocar mediante Invocación de plataforma.Para ello, me he adaptado Mohammad Elsheimy's code para llamar CreateFileTransacted():

// using System.ComponentModel; 
// using System.Runtime.InteropServices; 
// using System.Transactions; 

[ComImport] 
[Guid("79427a2b-f895-40e0-be79-b57dc82ed231")] 
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] 
public interface IKernelTransaction 
{ 
    void GetHandle(out IntPtr pHandle); 
} 

// 2.2 Win32 Error Codes <http://msdn.microsoft.com/en-us/library/cc231199.aspx> 
public const int ERROR_PATH_NOT_FOUND = 0x3; 
public const int ERROR_ALREADY_EXISTS = 0xb7; 
public const int ERROR_EFS_NOT_ALLOWED_IN_TRANSACTION = 0x1aaf; 

[DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Auto)] 
public static extern bool CreateDirectoryTransacted(string lpTemplateDirectory, string lpNewDirectory, IntPtr lpSecurityAttributes, IntPtr hTransaction); 

/// <summary> 
/// Creates a uniquely-named directory in the directory named by <paramref name="tempPath"/> and returns the path to it. 
/// </summary> 
/// <param name="tempPath">Path of a directory in which the temporary directory will be created.</param> 
/// <returns>The path of the newly-created temporary directory within <paramref name="tempPath"/>.</returns> 
public static string GetTempDirectoryName(string tempPath) 
{ 
    string retPath; 

    using (TransactionScope transactionScope = new TransactionScope()) 
    { 
     IKernelTransaction kernelTransaction = (IKernelTransaction)TransactionInterop.GetDtcTransaction(Transaction.Current); 
     IntPtr hTransaction; 
     kernelTransaction.GetHandle(out hTransaction); 

     while (!CreateDirectoryTransacted(null, retPath = Path.Combine(tempPath, Path.GetRandomFileName()), IntPtr.Zero, hTransaction)) 
     { 
      int lastWin32Error = Marshal.GetLastWin32Error(); 
      switch (lastWin32Error) 
      { 
       case ERROR_ALREADY_EXISTS: 
        break; 
       default: 
        throw new Win32Exception(lastWin32Error); 
      } 
     } 

     transactionScope.Complete(); 
    } 
    return retPath; 
} 

/// <summary> 
/// Equivalent to <c>GetTempDirectoryName(Path.GetTempPath())</c>. 
/// </summary> 
/// <seealso cref="GetTempDirectoryName(string)"/> 
public static string GetTempDirectoryName() 
{ 
    return GetTempDirectoryName(Path.GetTempPath()); 
} 
0
Dim NewFolder = System.IO.Directory.CreateDirectory(IO.Path.Combine(IO.Path.GetTempPath, Guid.NewGuid.ToString)) 
0

@JonathanWright sugiere CreateDirectory fallará cuando ya hay una carpeta. Si leo Directory.CreateDirectory, dice 'Este objeto se devuelve independientemente de si ya existe un directorio en la ruta especificada'. Lo que significa que no se detecta una carpeta creada entre verificar y crear.

Me gusta el CreateDirectoryTransacted() sugerido por @DanielTrebbien pero esta función está obsoleta.

La única solución que veo que queda es utilizar la c api y llamar al 'CreateDirectory' como error si la carpeta existe si realmente necesita asegurarse de cubrir toda la condición de carrera. Eso daría como resultado algo como esto:

Private Function GetTempFolder() As String 
    Dim folder As String 
    Dim succes as Boolean = false 
    Do While not succes 
     folder = Path.Combine(Path.GetTempPath, Path.GetRandomFileName) 
     success = c_api_create_directory(folder) 
    Loop 
    Return folder 
End Function 
+0

Una comprobación de bucle infinitamente sería sensato. – Matt

Cuestiones relacionadas