Usar o .NET para gerenciar diretórios e arquivos no Azure Data Lake Storage Gen2

Este artigo mostra como usar o .NET para criar e gerenciar diretórios e arquivos nas contas de armazenamento que têm um namespace hierárquico.

Para saber mais sobre como obter, definir e atualizar as ACLs (listas de controle de acesso) de diretórios e arquivos, veja Usar o .NET para gerenciar as ACLs no Azure Data Lake Storage Gen2.

Pacote (NuGet) | Exemplos | Referência de API | Mapeamento de Gen1 para Gen2 | Enviar comentários

Pré-requisitos

Configurar o seu projeto

Para começar, instale o pacote NuGet Azure.Storage.Files.DataLake.

Para obter mais informações sobre como instalar pacotes NuGet, veja Instalar e gerenciar pacotes no Visual Studio usando o Gerenciador de pacotes NuGet.

Em seguida, adicione o seguinte usando as instruções na parte superior do arquivo de código.

using Azure;
using Azure.Storage.Files.DataLake;
using Azure.Storage.Files.DataLake.Models;
using Azure.Storage;
using System.IO;

Observação

O acesso a vários protocolos no Data Lake Storage permite que os aplicativos usem APIs de Blob e APIs do Data Lake Storage Gen2 para trabalhar com dados em contas de armazenamento com o namespace hierárquico (HNS) habilitado. Ao trabalhar com recursos exclusivos do Data Lake Storage Gen2, como operações de diretório e ACLs, use as APIs do Data Lake Storage Gen2, conforme mostrado neste artigo.

Ao escolher quais APIs usar em um determinado cenário, considere a carga de trabalho e as necessidades do aplicativo, juntamente com os problemas conhecidos e o impacto do HNS em cargas de trabalho e aplicativos.

Autorizar o acesso e conectar-se a recursos de dados

Para trabalhar com os exemplos de código neste artigo, você precisa criar uma instância de DataLakeServiceClient autorizada que represente a conta de armazenamento. Você pode autorizar um objeto DataLakeServiceClient usando o Microsoft Entra ID, uma chave de acesso de conta ou uma Assinatura de Acesso Compartilhado (SAS).

Você pode usar a biblioteca de clientes de identidade do Azure para .NET para autenticar seu aplicativo com a ID do Microsoft Entra.

Crie uma instância DataLakeServiceClient e passe em uma nova instância da classe DefaultAzureCredential.

public static DataLakeServiceClient GetDataLakeServiceClient(string accountName)
{
    string dfsUri = $"https://{accountName}.dfs.core.windows.net";

    DataLakeServiceClient dataLakeServiceClient = new DataLakeServiceClient(
        new Uri(dfsUri),
        new DefaultAzureCredential());

    return dataLakeServiceClient;
}

Para saber mais sobre como usar a DefaultAzureCredential para autorizar o acesso a dados, confira Como autenticar aplicativos .NET com os serviços do Azure.

Criar um contêiner

Um contêiner atua como um sistema de arquivos para seus arquivos. Você pode criar um contêiner usando o método a seguir:

Esse exemplo cria um contêiner e retorna um objeto DataLakeFileSystemClient para uso posterior:

public async Task<DataLakeFileSystemClient> CreateFileSystem(
    DataLakeServiceClient serviceClient,
    string fileSystemName)
{
    return await serviceClient.CreateFileSystemAsync(fileSystemName);
}

Criar um diretório

Você pode criar uma referência de diretório no contêiner usando o método a seguir:

O exemplo de código a seguir adiciona um diretório a um contêiner e, em seguida, adiciona um subdiretório e retorna um objeto DataLakeDirectoryClient para uso posterior:

public async Task<DataLakeDirectoryClient> CreateDirectory(
    DataLakeFileSystemClient fileSystemClient,
    string directoryName,
    string subdirectoryName)
{
    DataLakeDirectoryClient directoryClient =
        await fileSystemClient.CreateDirectoryAsync(directoryName);

    return await directoryClient.CreateSubDirectoryAsync(subdirectoryName);
}

Renomear ou mover um diretório

Você pode renomear ou mover um diretório usando o método a seguir:

Passe o caminho do diretório desejado em um parâmetro. O seguinte exemplo de código mostra como renomear um subdiretório:

public async Task<DataLakeDirectoryClient> RenameDirectory(
    DataLakeFileSystemClient fileSystemClient,
    string directoryPath,
    string subdirectoryName,
    string subdirectoryNameNew)
{
    DataLakeDirectoryClient directoryClient =
        fileSystemClient.GetDirectoryClient(string.Join('/', directoryPath, subdirectoryName));

    return await directoryClient.RenameAsync(string.Join('/', directoryPath, subdirectoryNameNew));
}

O exemplo de código a seguir mostra como mover um subdiretório de um diretório para outro:

public async Task<DataLakeDirectoryClient> MoveDirectory(
    DataLakeFileSystemClient fileSystemClient,
    string directoryPathFrom,
    string directoryPathTo,
    string subdirectoryName)
{
    DataLakeDirectoryClient directoryClient =
         fileSystemClient.GetDirectoryClient(string.Join('/', directoryPathFrom, subdirectoryName));

    return await directoryClient.RenameAsync(string.Join('/', directoryPathTo, subdirectoryName));
}

Carregar um arquivo em um diretório

Você pode fazer upload de conteúdo para um arquivo novo ou existente usando o seguinte método:

O exemplo de código a seguir mostra como fazer upload de um arquivo local para um diretório usando o método UploadAsync:

public async Task UploadFile(
    DataLakeDirectoryClient directoryClient,
    string fileName,
    string localPath)
{
    DataLakeFileClient fileClient = 
        directoryClient.GetFileClient(fileName);

    FileStream fileStream = File.OpenRead(localPath);

    await fileClient.UploadAsync(content: fileStream, overwrite: true);
}

Você pode usar esse método para criar e carregar conteúdos em um novo arquivo ou pode definir o parâmetro overwrite como true para substituir um arquivo existente.

Acrescentar dados a um arquivo

Você pode carregar os dados que serão acrescentados a um arquivo usando o seguinte método:

O seguinte exemplo de código mostra como acrescentar dados ao final de um arquivo usando essas etapas:

public async Task AppendDataToFile(
    DataLakeDirectoryClient directoryClient,
    string fileName,
    Stream stream)
{
    DataLakeFileClient fileClient = 
        directoryClient.GetFileClient(fileName);

    long fileSize = fileClient.GetProperties().Value.ContentLength;

    await fileClient.AppendAsync(stream, offset: fileSize);

    await fileClient.FlushAsync(position: fileSize + stream.Length);
}

Baixar de um diretório

O exemplo de código a seguir mostra como baixar um arquivo de um diretório para um arquivo local usando estas etapas:

  • Crie uma instância DataLakeFileClient para representar o arquivo que você pretende baixar.
  • Use o método DataLakeFileClient.ReadAsync e, em seguida, analise o valor de retorno para obter um objeto Stream. Use qualquer API de processamento de arquivo .NET para salvar bytes do fluxo para um arquivo.

Este exemplo usa um BinaryReader e um FileStream para salvar bytes em um arquivo.

public async Task DownloadFile(
    DataLakeDirectoryClient directoryClient,
    string fileName,
    string localPath)
{
    DataLakeFileClient fileClient =
        directoryClient.GetFileClient(fileName);

    Response<FileDownloadInfo> downloadResponse = await fileClient.ReadAsync();

    BinaryReader reader = new BinaryReader(downloadResponse.Value.Content);

    FileStream fileStream = File.OpenWrite(localPath);

    int bufferSize = 4096;

    byte[] buffer = new byte[bufferSize];

    int count;

    while ((count = reader.Read(buffer, 0, buffer.Length)) != 0)
    {
        fileStream.Write(buffer, 0, count);
    }

    await fileStream.FlushAsync();

    fileStream.Close();
}

Listar conteúdo do diretório

Você pode listar o conteúdo do diretório usando o método a seguir e enumerando o resultado:

A enumeração dos caminhos no resultado pode fazer várias solicitações ao serviço durante a busca dos valores.

Esse exemplo de código imprime os nomes de cada arquivo localizado em um diretório:

public async Task ListFilesInDirectory(
    DataLakeFileSystemClient fileSystemClient,
    string directoryName)
{
    IAsyncEnumerator<PathItem> enumerator =
        fileSystemClient.GetPathsAsync(directoryName).GetAsyncEnumerator();

    await enumerator.MoveNextAsync();

    PathItem item = enumerator.Current;

    while (item != null)
    {
        Console.WriteLine(item.Name);

        if (!await enumerator.MoveNextAsync())
        {
            break;
        }

        item = enumerator.Current;
    }

}

Excluir um diretório

Você pode excluir um diretório usando o método a seguir:

O exemplo de código a seguir mostra como excluir um diretório:

public async Task DeleteDirectory(
    DataLakeFileSystemClient fileSystemClient,
    string directoryName)
{
    DataLakeDirectoryClient directoryClient =
        fileSystemClient.GetDirectoryClient(directoryName);

    await directoryClient.DeleteAsync();
}

Restaurar um diretório com exclusão reversível

Você pode usar as bibliotecas de clientes do Armazenamento do Microsoft Azure para restaurar um diretório excluído de maneira reversível. Use o seguinte método para listar caminhos excluídos para uma instância DataLakeFileSystemClient:

Use o seguinte método para restaurar um diretório com exclusão reversível:

O exemplo de código a seguir mostra como listar caminhos excluídos e restaurar um diretório excluído de forma reversível:

public async Task RestoreDirectory(
    DataLakeFileSystemClient fileSystemClient,
    string directoryName)
{
    DataLakeDirectoryClient directoryClient =
        fileSystemClient.GetDirectoryClient(directoryName);

    // List deleted paths
    List<PathDeletedItem> deletedItems = new List<PathDeletedItem>();
    await foreach (PathDeletedItem deletedItem in fileSystemClient.GetDeletedPathsAsync(directoryName))
    {
        deletedItems.Add(deletedItem);
    }

    // Restore deleted directory
    Response<DataLakePathClient> restoreResponse = await fileSystemClient.UndeletePathAsync(
        deletedItems[0].Path,
        deletedItems[0].DeletionId);
}

Se você renomear o diretório que contém os itens excluídos temporariamente, esses itens serão desconectados do diretório. Para restaurar esses itens, será preciso reverter o nome do diretório para o nome original ou criar outro diretório que use o nome original. Caso contrário, você receberá um erro ao tentar restaurar esses itens excluídos temporariamente.

Criar uma SAS de delegação de usuário para um diretório

Para trabalhar com os exemplos de código nesta seção, adicione a seguinte diretiva using:

using Azure.Storage.Sas;

O exemplo de código a seguir mostra como gerar uma SAS de delegação de usuário para um diretório quando um namespace hierárquico está habilitado para a conta de armazenamento:

async static Task<Uri> GetUserDelegationSasDirectory(DataLakeDirectoryClient directoryClient)
{
    try
    {
        // Get service endpoint from the directory URI.
        DataLakeUriBuilder dataLakeServiceUri = new DataLakeUriBuilder(directoryClient.Uri)
        {
            FileSystemName = null,
            DirectoryOrFilePath = null
        };

        // Get service client.
        DataLakeServiceClient dataLakeServiceClient =
            new DataLakeServiceClient(dataLakeServiceUri.ToUri(),
                                      new DefaultAzureCredential());

        // Get a user delegation key that's valid for seven days.
        // You can use the key to generate any number of shared access signatures 
        // over the lifetime of the key.
        Azure.Storage.Files.DataLake.Models.UserDelegationKey userDelegationKey =
            await dataLakeServiceClient.GetUserDelegationKeyAsync(DateTimeOffset.UtcNow,
                                                                  DateTimeOffset.UtcNow.AddDays(7));

        // Create a SAS token that's valid for seven days.
        DataLakeSasBuilder sasBuilder = new DataLakeSasBuilder()
        {
            // Specify the file system name and path, and indicate that
            // the client object points to a directory.
            FileSystemName = directoryClient.FileSystemName,
            Resource = "d",
            IsDirectory = true,
            Path = directoryClient.Path,
            ExpiresOn = DateTimeOffset.UtcNow.AddDays(7)
        };

        // Specify racwl permissions for the SAS.
        sasBuilder.SetPermissions(
            DataLakeSasPermissions.Read |
            DataLakeSasPermissions.Add |
            DataLakeSasPermissions.Create |
            DataLakeSasPermissions.Write |
            DataLakeSasPermissions.List
            );

        // Construct the full URI, including the SAS token.
        DataLakeUriBuilder fullUri = new DataLakeUriBuilder(directoryClient.Uri)
        {
            Sas = sasBuilder.ToSasQueryParameters(userDelegationKey,
                                                  dataLakeServiceClient.AccountName)
        };

        Console.WriteLine("Directory user delegation SAS URI: {0}", fullUri);
        Console.WriteLine();
        return fullUri.ToUri();
    }
    catch (Exception e)
    {
        Console.WriteLine(e.Message);
        throw;
    }
}

O exemplo a seguir testa a SAS de delegação de usuário criada no exemplo anterior de um aplicativo cliente simulado. Se a SAS for válida, o aplicativo cliente poderá listar os caminhos de arquivo para esse diretório. Se a SAS for inválida (por exemplo, a SAS tiver expirado), o serviço de Armazenamento retornará o código de erro 403 (Proibido).

private static async Task ListFilesPathsWithDirectorySasAsync(Uri sasUri)
{
    // Try performing an operation using the directory SAS provided.

    // Create a directory client object for listing operations.
    DataLakeDirectoryClient dataLakeDirectoryClient = new DataLakeDirectoryClient(sasUri);

    // List file paths in the directory.
    try
    {
        // Call the listing operation and return pages of the specified size.
        var resultSegment = dataLakeDirectoryClient.GetPathsAsync(false, false).AsPages();

        // Enumerate the file paths returned with each page.
        await foreach (Page<PathItem> pathPage in resultSegment)
        {
            foreach (PathItem pathItem in pathPage.Values)
            {
                Console.WriteLine("File name: {0}", pathItem.Name);
            }
            Console.WriteLine();
        }

        Console.WriteLine();
        Console.WriteLine("Directory listing operation succeeded for SAS {0}", sasUri);
    }
    catch (RequestFailedException e)
    {
        // Check for a 403 (Forbidden) error. If the SAS is invalid, 
        // Azure Storage returns this error.
        if (e.Status == 403)
        {
            Console.WriteLine("Directory listing operation failed for SAS {0}", sasUri);
            Console.WriteLine("Additional error information: " + e.Message);
            Console.WriteLine();
        }
        else
        {
            Console.WriteLine(e.Message);
            Console.ReadLine();
            throw;
        }
    }
}

Para saber mais sobre como criar uma SAS de delegação de usuário, confira Criar uma SAS de delegação de usuário com .NET.

Criar um serviço SAS para um diretório

Em uma conta de armazenamento com um namespace hierárquico habilitado, você pode criar uma SAS de serviço para um diretório. Para criar a SAS do serviço, certifique-se de que ter instalado a versão 12.5.0 ou posterior do pacote Azure.Storage.Files.DataLake.

O exemplo a seguir mostra como criar um SAS de serviço para um diretório:

private static Uri GetServiceSasUriForDirectory(DataLakeDirectoryClient directoryClient,
                                          string storedPolicyName = null)
{
    if (directoryClient.CanGenerateSasUri)
    {
        // Create a SAS token that's valid for one hour.
        DataLakeSasBuilder sasBuilder = new DataLakeSasBuilder()
        {
            // Specify the file system name, the path, and indicate that
            // the client object points to a directory.
            FileSystemName = directoryClient.FileSystemName,
            Resource = "d",
            IsDirectory = true,
            Path = directoryClient.Path,
        };

        // If no stored access policy is specified, create the policy
        // by specifying expiry and permissions.
        if (storedPolicyName == null)
        {
            sasBuilder.ExpiresOn = DateTimeOffset.UtcNow.AddHours(1);
            sasBuilder.SetPermissions(DataLakeSasPermissions.Read |
                DataLakeSasPermissions.Write |
                DataLakeSasPermissions.List);
        }
        else
        {
            sasBuilder.Identifier = storedPolicyName;
        }

        // Get the SAS URI for the specified directory.
        Uri sasUri = directoryClient.GenerateSasUri(sasBuilder);
        Console.WriteLine("SAS URI for ADLS directory is: {0}", sasUri);
        Console.WriteLine();

        return sasUri;
    }
    else
    {
        Console.WriteLine(@"DataLakeDirectoryClient must be authorized with Shared Key 
                          credentials to create a service SAS.");
        return null;
    }
}

Para saber mais sobre como criar uma SAS de serviço, confira Criar uma SAS de serviço com .NET.

Confira também