Verificando se existe um blob no Armazenamento do Azure


131

Eu tenho uma pergunta muito simples (espero!) - Eu só quero descobrir se um blob (com um nome que eu defini) existe em um contêiner específico. Vou fazer o download, se existir, e se não existir, farei outra coisa.

Eu fiz algumas pesquisas nos intertubos e, aparentemente, costumava haver uma função chamada DoesExist ou algo semelhante ... mas, como em muitas APIs do Azure, isso não parece mais estar lá (ou, se estiver, tem um nome muito disfarçado).


Obrigado a todos. Como estou usando o StorageClient (e preferia manter todo o meu acesso ao Armazenamento do Azure passando por essa biblioteca), fui com o método FetchAttributes-and-check-for-exceptions que a smarx sugeriu. Ele faz 'sentir' um pouco fora, em que eu não gosto de ter exceções lançadas como uma parte normal da minha lógica de negócios - mas espero que isso pode ser corrigido em uma futura versão StorageClient :)
John

Respostas:


202

A nova API possui a chamada de função .Exists (). Apenas certifique-se de usar o GetBlockBlobReference, que não executa a chamada para o servidor. Torna a função tão fácil quanto:

public static bool BlobExistsOnCloud(CloudBlobClient client, 
    string containerName, string key)
{
     return client.GetContainerReference(containerName)
                  .GetBlockBlobReference(key)
                  .Exists();  
}

6
Existe ... uma versão python?
Anpatel

2
Você quer saber o que você é cobrado pela verificação de blob? Essa defo parece ser o melhor caminho a percorrer do que tentar baixar o blob.
DermFrench 23/09

10
@anpatel, versão python:len(blob_service.list_blobs(container_name, file_name)) > 0
RaSi 06/04

3
você pode atualizar sua resposta com a qual pacote NuGet deve ser instalado
batmaci

9
NOTA: No Microsoft.WindowsAzure.Storage versão 8.1.4.0 (.Net Framework v4.6.2), o método Exists () não existe a favor de ExistsAsync (), que é a versão que será instalada nos projetos .NetCore
Adam Hardy

49

Nota: Esta resposta está desatualizada agora. Por favor, veja a resposta de Richard para uma maneira fácil de verificar a existência

Não, você não está perdendo algo simples ... fizemos um bom trabalho ao ocultar esse método na nova biblioteca StorageClient. :)

Acabei de escrever uma postagem no blog para responder sua pergunta: http://blog.smarx.com/posts/testing-existence-of-a-windows-azure-blob .

A resposta curta é: use CloudBlob.FetchAttributes (), que faz uma solicitação HEAD no blob.


1
FetchAttributes () leva muito tempo para ser executado (pelo menos no armazenamento de desenvolvimento) se o arquivo ainda não foi totalmente confirmado, ou seja, consiste apenas em blocos não confirmados.
Tom Robinson

7
Se você deseja obter o blob como o OP pretende fazer, por que não tentar fazer o download do conteúdo imediatamente? Se não estiver lá, será lançado como FetchAttributes. Fazer essa verificação primeiro é apenas uma solicitação extra ou estou faltando alguma coisa?
Marnix van Valen

Marnix faz uma excelente observação. Se você deseja fazer o download de qualquer maneira, tente fazer o download.
user94559

@ Marnix: Se você ligar para algo assim, OpenReadele não lançará ou retornará um fluxo vazio ou algo assim. Você só receberá erros quando iniciar o download. É muito mais fácil lidar com isso tudo em um só lugar :)
porges

1
@ Porges: projetar aplicativos em nuvem tem tudo a ver com "projetar para falhas". Há muitas discussões sobre como lidar adequadamente com essa situação. Mas, em geral - eu também iria baixá-lo e depois lidar com os erros de Blob ausentes. Não apenas isso, mas se vou verificar a existência de todos os blob, estou aumentando o número de transações de armazenamento e, portanto, a minha conta. Você ainda pode ter um local para lidar com exceções / erros.
Astaykov

16

Parece lamentável que você precise capturar uma exceção para testar se o blob existe.

public static bool Exists(this CloudBlob blob)
{
    try
    {
        blob.FetchAttributes();
        return true;
    }
    catch (StorageClientException e)
    {
        if (e.ErrorCode == StorageErrorCode.ResourceNotFound)
        {
            return false;
        }
        else
        {
            throw;
        }
    }
}

9

Se o blob for público, é claro, você pode simplesmente enviar uma solicitação HTTP HEAD - de qualquer um dos zilhões de idiomas / ambientes / plataformas que sabem como fazer isso - e verificar a resposta.

As principais APIs do Azure são interfaces HTTP baseadas em XML RESTful. A biblioteca StorageClient é um dos muitos wrappers possíveis em torno deles. Aqui está outro que Sriram Krishnan fez em Python:

http://www.sriramkrishnan.com/blog/2008/11/python-wrapper-for-windows-azure.html

Também mostra como se autenticar no nível HTTP.

Fiz uma coisa semelhante em C #, porque prefiro ver o Azure pelas lentes de HTTP / REST do que pelas lentes da biblioteca StorageClient. Por um bom tempo, eu nem me preocupei em implementar um método ExistsBlob. Todos os meus blobs eram públicos, e era trivial fazer o HTTP HEAD.


5

A nova Biblioteca de Armazenamento do Windows Azure já contém o método Exist (). Está no Microsoft.WindowsAzure.Storage.dll.

Disponível como pacote NuGet
Criado por: Microsoft
Id: WindowsAzure.Storage
Versão: 2.0.5.1

Veja também msdn


2

Se você não gosta de usar o método de exceção, a versão básica do c # que Judell sugere está abaixo. Cuidado, porém, que você também deve lidar com outras respostas possíveis.

HttpWebRequest myReq = (HttpWebRequest)WebRequest.Create(url);
myReq.Method = "HEAD";
HttpWebResponse myResp = (HttpWebResponse)myReq.GetResponse();
if (myResp.StatusCode == HttpStatusCode.OK)
{
    return true;
}
else
{
    return false;
}

4
HttpWebRequest.GetResponse lança uma exceção se houver um 404. Portanto, não vejo como seu código contornaria a necessidade de lidar com exceções?
Nitramk

Ponto justo. Parece-me um lixo que GetResponse () joga nesse ponto! Eu esperaria que ele retornasse o 404, pois essa é a resposta !!!
Mad Pierre

2

Se seu blob for público e você precisar apenas de metadados:

        HttpWebRequest request = (HttpWebRequest)WebRequest.Create(url);
        request.Method = "HEAD";
        string code = "";
        try
        {
            HttpWebResponse response = (HttpWebResponse)request.GetResponse();
            code = response.StatusCode.ToString();
        }
        catch 
        {
        }

        return code; // if "OK" blob exists


1

É assim que eu faço. Mostrando o código completo para quem precisa.

        // Parse the connection string and return a reference to the storage account.
        CloudStorageAccount storageAccount = CloudStorageAccount.Parse(CloudConfigurationManager.GetSetting("AzureBlobConnectionString"));

        CloudBlobClient blobClient = storageAccount.CreateCloudBlobClient();

        // Retrieve reference to a previously created container.
        CloudBlobContainer container = blobClient.GetContainerReference("ContainerName");

        // Retrieve reference to a blob named "test.csv"
        CloudBlockBlob blockBlob = container.GetBlockBlobReference("test.csv");

        if (blockBlob.Exists())
        {
          //Do your logic here.
        }

1

Embora a maioria das respostas aqui seja tecnicamente correta, a maioria dos exemplos de código está fazendo chamadas síncronas / bloqueadas. A menos que você esteja vinculado a uma plataforma ou base de código muito antiga, as chamadas HTTP sempre devem ser feitas de forma assíncrona e o SDK oferece suporte total a esse caso. Basta usar em ExistsAsync()vez de Exists().

bool exists = await client.GetContainerReference(containerName)
    .GetBlockBlobReference(key)
    .ExistsAsync();

Você está correto, o antigo .Exists () não é a melhor opção. No entanto, enquanto a antiga API é síncrono, utilizando aguardam provoca ExistsAsync também ser síncrono. Portanto, eu concordo que as chamadas HTTP geralmente devem ser assíncronas. Mas esse código não é esse. Ainda assim, +1 para a nova API!
Richard

2
Obrigado, mas não podia discordar mais. Exists()é síncrono, pois bloqueia um encadeamento até que seja concluído. await ExistsAscyn()é assíncrono, pois não. Ambos seguem o mesmo fluxo lógico em que a próxima linha de código não começa até que a anterior seja concluída, mas é a natureza não-bloqueadora ExistsAsyncque a torna assíncrona.
Todd Menier


1

Aqui está uma solução diferente se você não gostar das outras soluções:

Estou usando a versão 12.4.1 do pacote Azure.Storage.Blobs NuGet.

Recebo um objeto Azure.Pageable que é uma lista de todos os blobs em um contêiner. Em seguida, verifico se o nome do BlobItem é igual à propriedade Name de cada blob dentro do contêiner utilizando o LINQ . (Se tudo é válido, é claro)

using Azure.Storage.Blobs;
using Azure.Storage.Blobs.Models;
using System.Linq;
using System.Text.RegularExpressions;

public class AzureBlobStorage
{
    private BlobServiceClient _blobServiceClient;

    public AzureBlobStorage(string connectionString)
    {
        this.ConnectionString = connectionString;
        _blobServiceClient = new BlobServiceClient(this.ConnectionString);
    }

    public bool IsContainerNameValid(string name)
    {
        return Regex.IsMatch(name, "^[a-z0-9](?!.*--)[a-z0-9-]{1,61}[a-z0-9]$", RegexOptions.Singleline | RegexOptions.CultureInvariant);
    }

    public bool ContainerExists(string name)
    {
        return (IsContainerNameValid(name) ? _blobServiceClient.GetBlobContainerClient(name).Exists() : false);
    }

    public Azure.Pageable<BlobItem> GetBlobs(string containerName, string prefix = null)
    {
        try
        {
            return (ContainerExists(containerName) ? 
                _blobServiceClient.GetBlobContainerClient(containerName).GetBlobs(BlobTraits.All, BlobStates.All, prefix, default(System.Threading.CancellationToken)) 
                : null);
        }
        catch
        {
            throw;
        }
    }

    public bool BlobExists(string containerName, string blobName)
    {
        try
        {
            return (from b in GetBlobs(containerName)
                     where b.Name == blobName
                     select b).FirstOrDefault() != null;
        }
        catch
        {
            throw;
        }
    }
}

Espero que isso ajude alguém no futuro.

Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.