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

Juan
fuente
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();  
}
Ricardo
fuente
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.

usuario94559
fuente
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;
        }
    }
}
nathanw
fuente
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.

judell
fuente
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

huha
fuente
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;
}
Pierre loco
fuente
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
emert117
fuente
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.
        }
Apolo
fuente
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();
Todd Menier
fuente
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.

Zaehos
fuente