Comprobar si existe un blob en Azure Storage


131

Tengo una pregunta muy simple (¡espero!): Solo quiero saber si existe un blob (con un nombre que he definido) en un contenedor en particular. Lo estaré descargando si existe, y si no existe, haré otra cosa.

He hecho algunas búsquedas en los intertubos y aparentemente solía haber una función llamada DoesExist o algo similar ... pero como con muchas de las API de Azure, esto ya no parece estar allí (o si lo está, tiene un nombre muy hábilmente disfrazado).


Gracias a todos. Como estoy usando StorageClient (y preferiría mantener todo mi acceso de Azure Storage a través de esa biblioteca), utilicé el método FetchAttributes-and-check-for-excepciones que sugirió smarx. Se siente un poco mal, ya que no me gusta que se generen excepciones como parte normal de mi lógica de negocios, pero espero que esto se pueda solucionar en una futura versión de StorageClient :)
John

Respuestas:


202

La nueva API tiene la llamada a la función .Exists (). Solo asegúrese de usar el GetBlockBlobReference, que no realiza la llamada al servidor. Hace que la función sea tan fácil como:

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

66
¿Hay ... una ... versión de Python?
anpatel

2
¿Se pregunta qué le cobran por comprobar que existe blob? Esta defo parece ser una mejor manera de ir que intentar descargar el blob.
DermFrench

10
@anpatel, versión de python:len(blob_service.list_blobs(container_name, file_name)) > 0
RaSi

3
puede actualizar su respuesta con qué paquete nuget debe instalarse
batmaci

9
NOTA: A partir de Microsoft.WindowsAzure.Storage versión 8.1.4.0 (.Net Framework v4.6.2) el método Exists () no existe a favor de ExistsAsync (), que es la versión que se instalará para proyectos .NetCore
Adam Hardy

49

Nota: esta respuesta está desactualizada ahora. Consulte la respuesta de Richard para ver una forma fácil de verificar la existencia

No, no te estás perdiendo algo simple ... hicimos un buen trabajo al ocultar este método en la nueva biblioteca StorageClient. :)

Acabo de escribir una publicación de blog para responder a su pregunta: http://blog.smarx.com/posts/testing-existence-of-a-windows-azure-blob .

La respuesta corta es: use CloudBlob.FetchAttributes (), que realiza una solicitud HEAD contra el blob.


1
FetchAttributes () tarda mucho tiempo en ejecutarse (al menos en el almacenamiento de desarrollo) si el archivo aún no se ha confirmado por completo, es decir, solo consta de bloques no confirmados.
Tom Robinson el

77
Si vas a buscar el blob de todos modos como lo hace el OP, ¿por qué no intentas descargar el contenido de inmediato? Si no está allí, arrojará como FetchAttributes. Hacer esta verificación primero es solo una solicitud adicional, ¿o me falta algo?
Marnix van Valen

Marnix hace un excelente punto. Si vas a descargarlo de todos modos, solo intenta descargarlo.
user94559

@ Marnix: si llamas a algo así OpenRead, no arrojará ni devolverá un Stream vacío o algo así. Solo obtendrá errores cuando comience a descargarlo. Es mucho más fácil manejar todo esto en un solo lugar :)
porges

1
@Porges: el diseño de aplicaciones en la nube se trata de "diseñar para fallar" Hay muchas discusiones sobre cómo manejar adecuadamente esta situación. Pero en general, también iría y lo descargaría, luego manejaría los errores de Blob que faltan. No solo eso, sino que si voy a verificar la existencia de cada gota, aumentaré el número de transacciones de almacenamiento, por lo tanto, mi factura. Todavía puede tener un lugar para manejar Excepciones / Errores.
astaykov

16

Parece lamentable que necesite atrapar una excepción para probar que el 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

Si el blob es público, por supuesto, solo puede enviar una solicitud HTTP HEAD, desde cualquiera de los millones de idiomas / entornos / plataformas que saben cómo hacerlo, y verificar la respuesta.

Las principales API de Azure son interfaces HTTP RESTful basadas en XML. La biblioteca StorageClient es uno de los muchos envoltorios posibles a su alrededor. Aquí hay otro que Sriram Krishnan hizo en Python:

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

También muestra cómo autenticarse en el nivel HTTP.

He hecho algo similar para mí en C #, porque prefiero ver Azure a través de la lente de HTTP / REST en lugar de a través de la lente de la biblioteca StorageClient. Durante bastante tiempo ni siquiera me había molestado en implementar un método ExistsBlob. Todos mis blobs eran públicos, y era trivial hacer HTTP HEAD.


5

La nueva Biblioteca de almacenamiento de Windows Azure ya contiene el método Exist (). Está en Microsoft.WindowsAzure.Storage.dll.

Disponible como paquete NuGet
Creado por: Microsoft
Id: WindowsAzure.
Versión de almacenamiento: 2.0.5.1

Ver también msdn


2

Si no le gusta usar el método de excepción, la versión básica de C # de lo que sugiere judell está a continuación. Sin embargo, tenga cuidado de que realmente también debería manejar otras posibles respuestas.

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

44
HttpWebRequest.GetResponse lanza una excepción si hay un 404. ¿Entonces no veo cómo su código evitaría la necesidad de manejar excepciones?
Nitramk

Punto justo. ¡Me parece una basura que GetResponse () arroja en ese punto! Espero que devuelva el 404 ya que esa es la respuesta.
Mad Pierre

2

Si su blob es público y solo necesita metadatos:

        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

Así es como lo hago. Mostrando código completo para aquellos que lo necesitan.

        // 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

Aunque la mayoría de las respuestas aquí son técnicamente correctas, la mayoría de los ejemplos de código están haciendo llamadas sincrónicas / bloqueantes. A menos que esté sujeto a una plataforma o base de código muy antigua, las llamadas HTTP siempre deben realizarse de forma asíncrona, y el SDK lo admite por completo en este caso. Solo use en ExistsAsync()lugar de Exists().

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

Tienes razón, el viejo .Exists () no es la mejor opción. Sin embargo, mientras que la antigua API es sincrónico, utilizando esperan ser causa ExistsAsync también a ser sincrónica. Por lo tanto, estaría de acuerdo en que las llamadas HTTP generalmente deberían ser asíncronas. Pero este código no es eso. Aún así, +1 para la nueva API!
Richard

2
Gracias, pero no podría estar más en desacuerdo. Exists()es sincrónico porque bloquea un hilo hasta que se completa. await ExistsAscyn()es asíncrono porque no lo hace. Ambos siguen el mismo flujo lógico en el sentido de que la siguiente línea de código no comienza hasta que se termina la anterior, pero es la naturaleza sin bloqueo lo ExistsAsyncque la hace asíncrona.
Todd Menier

1
Y ... ¡he aprendido algo nuevo! :) softwareengineering.stackexchange.com/a/183583/38547
Richard

1

Aquí hay una solución diferente si no le gustan las otras soluciones:

Estoy usando la versión 12.4.1 del paquete NuGet de Azure.Storage.Blobs.

Obtengo un objeto Azure.Pageable que es una lista de todos los blobs en un contenedor. Luego verifico si el nombre del BlobItem es igual a la propiedad Name de cada blob dentro del contenedor utilizando LINQ . (Si todo es válido, por supuesto)

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;
        }
    }
}

Esperemos que esto ayude a alguien en el futuro.

Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.