Verifica dell'esistenza di un BLOB nell'archiviazione di Azure


131

Ho una domanda molto semplice (spero!) - Voglio solo scoprire se esiste un BLOB (con un nome che ho definito) in un particolare contenitore. Lo scaricherò se esiste e, in caso contrario, farò qualcos'altro.

Ho fatto qualche ricerca sugli intertubes e apparentemente c'era una funzione chiamata DoesExist o qualcosa di simile ... ma come con così tante API di Azure, questo non sembra più essere lì (o, se lo è, ha un nome molto abilmente mascherato).


Grazie a tutti. Dato che sto usando StorageClient (e preferirei che tutto il mio accesso all'archiviazione di Azure passasse attraverso quella libreria) sono andato con il metodo FetchAttributes-and-check-for-exceptions suggerito da Smarx. "Mi sento" un po 'fuori, in quanto non mi piace avere eccezioni gettate come parte normale della mia logica aziendale - ma spero che questo possa essere risolto in una futura versione di StorageClient :)
John

Risposte:


202

La nuova API ha la chiamata alla funzione .Exists (). Assicurati solo di usare il GetBlockBlobReference, che non esegue la chiamata al server. Rende la funzione semplice come:

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

6
C'è ... una ... versione di Python?
Anpatel,

2
Ti chiedi cosa ti viene addebitato per il controllo del BLOB? Questo defo sembra un modo migliore per andare rispetto al tentativo di scaricare il BLOB.
DermFrench,

10
@anpatel, versione python:len(blob_service.list_blobs(container_name, file_name)) > 0
RaSi

3
puoi aggiornare la tua risposta con quale pacchetto nuget deve essere installato
batmaci,

9
NOTA: A partire da Microsoft.WindowsAzure.Storage versione 8.1.4.0 (.Net Framework v4.6.2) il metodo Exists () non esiste a favore di ExistsAsync () che è la versione che verrà installata per i progetti .NetCore
Adam Hardy

49

Nota: questa risposta non è più aggiornata. Si prega di consultare la risposta di Richard per un modo semplice per verificare l'esistenza

No, non ti stai perdendo qualcosa di semplice ... abbiamo fatto un buon lavoro nel nascondere questo metodo nella nuova libreria StorageClient. :)

Ho appena scritto un post sul blog per rispondere alla tua domanda: http://blog.smarx.com/posts/testing-existence-of-a-windows-azure-blob .

La risposta breve è: utilizzare CloudBlob.FetchAttributes (), che esegue una richiesta HEAD sul BLOB.


1
L'esecuzione di FetchAttributes () richiede molto tempo (almeno nella memoria di sviluppo) se il file non è stato ancora eseguito il commit completo, ovvero consiste solo di blocchi non impegnati.
Tom Robinson,

7
Se hai intenzione di recuperare comunque il BLOB come l'OP intende fare, perché non provare a scaricare subito il contenuto? Se non è lì, verrà lanciato proprio come FetchAttributes. Fare prima questo controllo è solo una richiesta aggiuntiva o mi sto perdendo qualcosa?
Marnix van Valen,

Marnix fa un punto eccellente. Se lo scaricherai comunque, prova a scaricarlo.
user94559,

@Marnix: se chiami qualcosa del genere OpenReadnon verrà generato o restituito un flusso vuoto o qualcosa del genere. Riceverai errori solo quando inizi a scaricare da esso. È molto più facile gestire tutto in un unico posto :)
porges

1
@Porges: la progettazione di applicazioni cloud si basa sulla "progettazione per guasti". Ci sono molte discussioni su come gestire correttamente questa situazione. Ma in generale - Vorrei anche semplicemente scaricarlo, quindi gestire gli errori BLOB mancanti. Non solo, ma se verificherò l'esistenza ogni BLOB, aumenterò il numero di transazioni di archiviazione, quindi la mia fattura. Puoi ancora avere un posto per gestire le eccezioni / errori.
astaykov,

16

Sembra zoppo che è necessario catturare un'eccezione per testarlo esiste il BLOB.

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 il BLOB è pubblico, ovviamente, puoi semplicemente inviare una richiesta HEAD HTTP - da uno qualsiasi dei miliardi di lingue / ambienti / piattaforme che sanno come farlo - e controllare la risposta.

Le API di Azure di base sono interfacce HTTP basate su XML RESTful. La libreria StorageClient è uno dei tanti wrapper possibili attorno a loro. Ecco un altro che ha fatto Sriram Krishnan in Python:

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

Mostra anche come eseguire l'autenticazione a livello HTTP.

Ho fatto una cosa simile per me stesso in C #, perché preferisco vedere Azure attraverso l'obiettivo di HTTP / REST piuttosto che attraverso l'obiettivo della libreria StorageClient. Per un bel po 'non mi ero nemmeno preso la briga di implementare un metodo ExistsBlob. Tutti i miei BLOB erano pubblici ed era banale eseguire HTTP HEAD.


5

La nuova libreria di archiviazione di Windows Azure contiene già il metodo Exist (). È in Microsoft.WindowsAzure.Storage.dll.

Disponibile come pacchetto NuGet
Creato da:
ID Microsoft : WindowsAzure.Storage
Versione: 2.0.5.1

Vedi anche msdn


2

Se non ti piace usare il metodo dell'eccezione, la versione c # di base di ciò che Judell suggerisce è di seguito. Attenzione però che dovresti davvero gestire anche altre possibili risposte.

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 genera un'eccezione se esiste un 404. Quindi non vedo come il tuo codice potrebbe eludere la necessità di gestire le eccezioni?
Nitramk,

Punto valido. Mi sembra spazzatura che GetResponse () lancia a quel punto! Mi aspetto che restituisca il 404 poiché questa è la risposta !!!
Mad Pierre,

2

Se il tuo BLOB è pubblico e hai bisogno solo di metadati:

        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

Questo è il modo in cui lo faccio. Mostra il codice completo per chi ne ha bisogno.

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

Sebbene la maggior parte delle risposte qui siano tecnicamente corrette, la maggior parte degli esempi di codice sta effettuando chiamate sincrone / bloccanti. A meno che tu non sia vincolato da una piattaforma o una base di codice molto vecchia, le chiamate HTTP dovrebbero sempre essere eseguite in modo asincrono e in questo caso l'SDK lo supporta completamente. Basta usare ExistsAsync()invece di Exists().

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

Hai ragione, il vecchio .Exists () non è l'opzione migliore. Tuttavia, mentre la vecchia API è sincrona, utilizzando attendono provoca ExistsAsync essere anche sincrona. Quindi, sarei d'accordo sul fatto che le chiamate HTTP dovrebbero di solito essere asincrone. Ma questo codice non è quello. Tuttavia, +1 per la nuova API!
Richard,

2
Grazie, ma non potrei essere più in disaccordo. Exists()è sincrono in quanto blocca un thread fino al completamento. await ExistsAscyn()è asincrono in quanto non lo è. Entrambi seguono lo stesso flusso logico in quanto la riga di codice successiva non inizia fino a quando non viene eseguita la precedente, ma è la natura non bloccante ExistsAsyncche la rende asincrona.
Todd Menier,

1
E ... ho imparato qualcosa di nuovo! :) softwareengineering.stackexchange.com/a/183583/38547
Richard

1

Ecco una soluzione diversa se non ti piacciono le altre soluzioni:

Sto usando la versione 12.4.1 del pacchetto NuGet Azure.Storage.Blobs.

Ottengo un oggetto Azure.Pageable che è un elenco di tutti i BLOB in un contenitore. Quindi controllo se il nome di BlobItem è uguale alla proprietà Name di ciascun BLOB all'interno del contenitore utilizzando LINQ . (Se tutto è valido, ovviamente)

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

Spero che questo aiuti qualcuno in futuro.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.