Pergunta

Estou trabalhando em um programa que precisa criar várias pastas temporárias para o aplicativo.Eles não serão vistos pelo usuário.O aplicativo foi escrito em VB.net.Posso pensar em algumas maneiras de fazer isso, como nome de pasta incremental ou nomes de pasta com números aleatórios, mas queria saber como outras pessoas resolvem esse problema?

Foi útil?

Solução

Atualizar: Adicionada verificação File.Exists por comentário (19 de junho de 2012)

Aqui está o que usei no VB.NET.Essencialmente igual ao apresentado, exceto que normalmente não queria criar a pasta imediatamente.

A vantagem de usar Obter nome de arquivo aleatório é que ele não cria um arquivo, então você não precisa limpar se estiver usando o nome para algo diferente de um arquivo.Como usá-lo para o nome da pasta.

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

Aleatório Exemplo de nome de arquivo:

C:\Documents and Settings ome de usuário\Configurações locais emp\u3z5e0co.tvq


Aqui está uma variação usando um Guid para obter o nome da pasta temporária.

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

guia Exemplo:

C:\Documents and Settings ome de usuário\Configurações locais emp\2dbc6db7-2d45-4b75-b27f-0bd492c60496

Outras dicas

Você tem que usar System.IO.Path.GetTempFileName()

Cria um arquivo temporário de zero byte com nome exclusivo no disco e retorna o caminho completo desse arquivo.

Você pode usar System.IO.Path.GetDirectoryName(System.IO.Path.GetTempFileName()) para obter apenas as informações da pasta temporária e criar suas pastas lá

Eles são criados na pasta temporária do Windows e isso é considerado uma prática recomendada

Só para esclarecer:

System.IO.Path.GetTempPath()

retorna apenas o caminho da pasta para a pasta temporária.

System.IO.Path.GetTempFileName()

retorna o nome completo do arquivo (incluindo o caminho), portanto:

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

é redundante.

Há uma possível condição de corrida quando:

  • criando um arquivo temporário com GetTempFileName(), excluindo-o e criando uma pasta com o mesmo nome, ou
  • usando GetRandomFileName() ou Guid.NewGuid.ToString para nomear uma pasta e criar a pasta mais tarde

Com GetTempFileName() após a exclusão, outro aplicativo poderá criar com êxito um arquivo temporário com o mesmo nome.O CreateDirectory() então falharia.

Da mesma forma, entre ligar GetRandomFileName() e criando o diretório, outro processo poderia criar um arquivo ou diretório com o mesmo nome, resultando novamente em CreateDirectory() falhando.

Para a maioria dos aplicativos, não há problema em um diretório temporário falhar devido a uma condição de corrida.Afinal, é extremamente raro.Para eles, estas raças podem muitas vezes ser ignoradas.

No mundo dos scripts de shell Unix, criar arquivos temporários e diretórios de maneira segura e livre de corridas é um grande negócio.Muitas máquinas têm vários usuários (hostis) - pense em host compartilhado - e muitos scripts e aplicativos precisam criar com segurança arquivos e diretórios temporários no diretório /tmp compartilhado.Ver Criando arquivos temporários com segurança em scripts Shell para uma discussão sobre como criar diretórios temporários com segurança a partir de scripts shell.

Como @JonathanWright apontou, existem condições de corrida para as soluções:

  • Crie um arquivo temporário com GetTempFileName(), exclua-o e crie uma pasta com o mesmo nome
  • Usar GetRandomFileName() ou Guid.NewGuid.ToString para criar um nome de pasta aleatório, verifique se ele existe e, caso contrário, crie-o.

É possível, no entanto, criar um diretório temporário exclusivo atomicamente utilizando o comando NTFS transacional (TxF)API.

TxF tem um CreateDirectoryTransacted() função que pode ser invocada via Platform Invoke.Para isso, adaptei Código de Mohammad Elsheimy para chamar 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());
}

Algo como...

using System.IO;

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

Directory.CreateDirectory(path);

Você pode gerar um GUID para os nomes das pastas temporárias.

Contanto que o nome da pasta não precise ser significativo, que tal usar um GUID para eles?

Você pode usar GetTempFileName para criar um temporário arquivo, e exclua e recrie esse arquivo como um diretório.

Observação:o link não funcionou, copie/cole de: http://msdn.microsoft.com/en-us/library/aa364991(VS.85).aspx

As respostas combinadas de @adam-wright e pix0r funcionarão melhor no IMHO:


using System.IO;

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

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

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

A vantagem de usar System.IO.Path.GetTempFileName é que ele será um arquivo no caminho local do usuário (ou seja, sem roaming).É exatamente aqui que você deseja por questões de permissões e segurança.

Dim NewFolder = System.IO.Directory.CreateDirectory(IO.Path.Combine(IO.Path.GetTempPath, Guid.NewGuid.ToString))

@JonathanWright sugere que CreateDirectory falhará quando já houver uma pasta.Se eu leio o diretório.CreateDirectory, ele diz: 'Este objeto é retornado, independentemente de existir um diretório no caminho especificado.' Significando que você não detecta uma pasta criada entre a verificação e a criação de realmente.

Gosto do CreateDirectoryTransacted() sugerido por @DanielTrebbien, mas esta função está obsoleta.

A única solução que vejo que resta é usar a API c e chamar o 'Criar diretório'lá, pois ocorre um erro se a pasta existir se você realmente precisar cobrir toda a condição de corrida.Isso resultaria em algo assim:

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
Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top