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).

John
fonte
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();  
}
Richard
fonte
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.

user94559
fonte
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;
        }
    }
}
nathanw
fonte
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.

Judell
fonte
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

huha
fonte
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;
}
Mad Pierre
fonte
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
emert117
fonte
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.
        }
Apollo
fonte
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();
Todd Menier
fonte
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
E ... eu aprendi algo novo! :) softwareengineering.stackexchange.com/a/183583/38547
Richard
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.

Zaehos
fonte