Sprawdzanie, czy obiekt blob istnieje w usłudze Azure Storage

136

Mam bardzo proste pytanie (mam nadzieję!) - chcę się tylko dowiedzieć, czy w konkretnym kontenerze istnieje blob (o zdefiniowanej przeze mnie nazwie). Pobiorę go, jeśli istnieje, a jeśli nie, zrobię coś innego.

Wyszukałem trochę w intertubach i najwyraźniej była tam funkcja o nazwie DoesExist lub coś podobnego ... ale podobnie jak w przypadku wielu interfejsów API platformy Azure, wydaje się, że już jej tam nie ma (lub jeśli tak, ma bardzo sprytnie zamaskowana nazwa).

Jan
źródło
Dziękuję wszystkim. Ponieważ używam StorageClient (i wolałbym, aby cały mój dostęp do usługi Azure Storage przechodził przez tę bibliotekę), wybrałem metodę FetchAttributes-and-check-for-exceptions, którą zasugerował smarx. Wydaje się to trochę dziwne, ponieważ nie lubię, gdy wyjątki są rzucane jako normalna część mojej logiki biznesowej - ale mam nadzieję, że można to naprawić w przyszłej wersji StorageClient :)
Jan

Odpowiedzi:

211

Nowe API ma wywołanie funkcji .Exists (). Tylko upewnij się, że używasz GetBlockBlobReference, który nie wykonuje wywołania do serwera. Dzięki temu funkcja jest tak prosta, jak:

public static bool BlobExistsOnCloud(CloudBlobClient client, 
    string containerName, string key)
{
     return client.GetContainerReference(containerName)
                  .GetBlockBlobReference(key)
                  .Exists();  
}
Richard
źródło
7
Czy istnieje ... wersja pythona?
anpatel
2
Zastanawiasz się, ile płacisz za sprawdzenie istnienia obiektu blob? Wydaje się, że to zdecydowanie lepsze rozwiązanie niż próba pobrania obiektu blob.
DermFrench,
10
@anpatel, wersja Pythona:len(blob_service.list_blobs(container_name, file_name)) > 0
RaSi
3
możesz zaktualizować swoją odpowiedź, z którym pakiet nuget powinien zostać zainstalowany
Emil
12
UWAGA: Od wersji Microsoft.WindowsAzure.Storage 8.1.4.0 (.Net Framework v4.6.2) metoda Exists () nie istnieje na korzyść ExistsAsync (), która jest wersją, która zostanie zainstalowana dla projektów .NetCore
Adam Hardy
50

Uwaga: ta odpowiedź jest teraz nieaktualna. Zapoznaj się z odpowiedzią Richarda, aby w łatwy sposób sprawdzić istnienie

Nie, nie brakuje Ci czegoś prostego ... wykonaliśmy dobrą robotę, ukrywając tę ​​metodę w nowej bibliotece StorageClient. :)

Właśnie napisałem post na blogu, aby odpowiedzieć na Twoje pytanie: http://blog.smarx.com/posts/testing-existence-of-a-windows-azure-blob .

Krótka odpowiedź brzmi: użyj CloudBlob.FetchAttributes (), która wykonuje żądanie HEAD względem obiektu BLOB.

user94559
źródło
1
FetchAttributes () trwa długo (przynajmniej w pamięci deweloperskiej), jeśli plik nie został jeszcze w pełni zatwierdzony, tj. Składa się tylko z niezatwierdzonych bloków.
Tom Robinson
7
Jeśli i tak zamierzasz pobrać obiekt blob, tak jak zamierza to zrobić OP, dlaczego nie spróbować od razu pobrać zawartości? Jeśli go tam nie ma, wyrzuci podobnie jak FetchAttributes. Wykonanie tego sprawdzenia w pierwszej kolejności to tylko dodatkowa prośba, czy czegoś mi brakuje?
Marnix van Valen
Marnix jest świetnym argumentem. Jeśli mimo wszystko masz zamiar go pobrać, po prostu spróbuj go pobrać.
user94559
@Marnix: Jeśli zadzwonisz do czegoś takiego OpenRead, nie wyrzuci ani nie zwróci pustego strumienia ani niczego w tym stylu. Błędy pojawią się tylko wtedy, gdy zaczniesz z niego pobierać. Dużo łatwiej poradzić sobie z tym wszystkim w jednym miejscu :)
porusza się
1
@Porges: w projektowaniu aplikacji w chmurze chodzi o „projektowanie pod kątem awarii”. Toczy się wiele dyskusji, jak właściwie poradzić sobie w tej sytuacji. Ale ogólnie - po prostu poszedłbym go i pobrać, a następnie zająłbym się brakującymi błędami Bloba. Nie tylko to, ale jeśli mam sprawdzić, czy istnieje każdy blob, zwiększam liczbę transakcji magazynu, a tym samym mój rachunek. Nadal możesz mieć jedno miejsce do obsługi wyjątków / błędów.
astaykov
16

Wydaje się kiepskie, że musisz złapać wyjątek, aby sprawdzić, czy obiekt blob istnieje.

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
źródło
9

Jeśli obiekt blob jest publiczny, możesz oczywiście po prostu wysłać żądanie HTTP HEAD - z dowolnego z milionów języków / środowisk / platform, które wiedzą, jak to zrobić - i sprawdzić odpowiedź.

Podstawowe interfejsy API platformy Azure są interfejsami HTTP opartymi na języku RESTful. Biblioteka StorageClient jest jednym z wielu możliwych opakowań wokół nich. Oto kolejny, który Sriram Krishnan zrobił w Pythonie:

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

Pokazuje również, jak uwierzytelniać się na poziomie HTTP.

Zrobiłem dla siebie podobną rzecz w C #, ponieważ wolę patrzeć na platformę Azure przez pryzmat HTTP / REST niż przez pryzmat biblioteki StorageClient. Od dłuższego czasu nawet nie zadałem sobie trudu, aby zaimplementować metodę ExistsBlob. Wszystkie moje obiekty blob były publiczne i zrobienie HTTP HEAD było trywialne.

Judell
źródło
5

Nowa biblioteka magazynu Windows Azure zawiera już metodę Exist (). Znajduje się w Microsoft.WindowsAzure.Storage.dll.

Dostępny jako pakiet NuGet
Utworzony przez: Microsoft
Id: WindowsAzure
Wersja magazynu: 2.0.5.1

Zobacz także msdn

huha
źródło
2

Jeśli nie lubisz używać metody wyjątku, podstawowa wersja C # tego, co sugeruje judell, jest poniżej. Uważaj jednak, że naprawdę powinieneś poradzić sobie również z innymi możliwymi odpowiedziami.

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
źródło
4
HttpWebRequest.GetResponse zgłasza wyjątek, jeśli istnieje 404. Więc nie widzę, jak Twój kod miałby obejść potrzebę obsługi wyjątków?
Nitramk
Słuszna uwaga. Wydaje mi się, że GetResponse () wyrzuca w tym momencie bzdury! Spodziewałbym się, że zwróci 404, ponieważ jest to odpowiedź !!!
Mad Pierre,
2

Jeśli Twój obiekt BLOB jest publiczny i potrzebujesz tylko metadanych:

        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
źródło
2

Oto inne rozwiązanie, jeśli nie lubisz innych rozwiązań:

Używam wersji 12.4.1 pakietu NuGet Azure.Storage.Blobs.

Otrzymuję Azure.Pageable przedmiot, który jest lista wszystkich bąble w pojemniku. Następnie sprawdzam, czy nazwa obiektu BlobItem jest równa właściwości Name każdego obiektu BLOB w kontenerze przy użyciu LINQ . (Jeśli wszystko jest prawidłowe, oczywiście)

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

Mam nadzieję, że pomoże to komuś w przyszłości.

Zaehos
źródło
1

Tak to robię. Wyświetlanie pełnego kodu dla tych, którzy go potrzebują.

        // 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
źródło
1

Chociaż większość odpowiedzi tutaj jest poprawnych technicznie, większość przykładów kodu wykonuje wywołania synchroniczne / blokujące. O ile nie jesteś związany bardzo starą platformą lub bazą kodu, wywołania HTTP powinny zawsze być wykonywane asynchronicznie, a zestaw SDK w tym przypadku w pełni je obsługuje. Po prostu użyj ExistsAsync()zamiast Exists().

bool exists = await client.GetContainerReference(containerName)
    .GetBlockBlobReference(key)
    .ExistsAsync();
Todd Menier
źródło
Masz rację, stara .Exists () nie jest najlepszą opcją. Jednakże, podczas gdy stare API jest synchroniczny, używając czekają powoduje ExistsAsync być również synchroniczny. Zgadzam się więc, że wywołania HTTP powinny być zwykle asynchroniczne. Ale ten kod to nie to. Mimo to +1 dla nowego interfejsu API!
Richard
2
Dzięki, ale nie mogłem się więcej nie zgodzić. Exists()jest synchroniczny, ponieważ blokuje wątek aż do zakończenia. await ExistsAscyn()jest asynchroniczny w tym sensie, że nie. Oba podążają za tym samym logicznym przepływem, ponieważ następny wiersz kodu nie zaczyna się, dopóki poprzedni nie zostanie ukończony, ale to nieblokujący charakter ExistsAsyncsprawia, że ​​jest on asynchroniczny.
Todd Menier
1
I ... Nauczyłem się czegoś nowego! :) softwareengineering.stackexchange.com/a/183583/38547
Richard
0

Dzięki bibliotece Azure Blob Storage w wersji 12 możesz używać platformy BlobBaseClient.Exists()/BlobBaseClient.ExistsAsync()

Odpowiedział na inne podobne pytanie: https://stackoverflow.com/a/63293998/4865541

Jaliya Udagedara
źródło
0

Wersja Java do tego samego (przy użyciu nowego zestawu SDK v12)

Wykorzystuje to autoryzację Shared Key Credential, która jest kluczem dostępu do konta.

    StorageSharedKeyCredential credential = new StorageSharedKeyCredential(accountName, accountKey);
    String endpoint = String.format(Locale.ROOT, "https://%s.blob.core.windows.net", accountName);
    BlobServiceClient storageClient = new BlobServiceClientBuilder().credential(credential)
                                          .endpoint(endpoint).buildClient();

    BlobContainerClient container = storageClient.getBlobContainerClient(containerName)
    if ( container.exists() ) {
       // perform operation when container exists 
    }         
Somansh Reddy
źródło