Azure / Azurite

A lightweight server clone of Azure Storage that simulates most of the commands supported by it with minimal dependencies
MIT License
1.8k stars 320 forks source link

[BUG] Retrieving Blob metadata with Blob SDK v12 always returns NULL #759

Open alberto-alvarado-m opened 4 years ago

alberto-alvarado-m commented 4 years ago

Describe the bug I can successfully add Metadata to a Blob using the Storage Blob SDK v12. I can see in Azure Portal and in the Storage Explorer that the metadata has been added to my blob, but when I retrieve the blob with the SDK, the metadata is always NULL.

Exception or Stack Trace No errors or exceptions when retrieving.

To Reproduce

Code Snippet

package com.blobs.quickstart;

import com.azure.core.http.HttpClient;
import com.azure.core.http.ProxyOptions;
import com.azure.core.http.netty.NettyAsyncHttpClientBuilder;
import com.azure.storage.blob.*;
import com.azure.storage.blob.models.*;

import org.apache.log4j.BasicConfigurator;
import org.apache.log4j.Level;
import org.apache.log4j.Logger;

import java.io.*;
import java.net.InetSocketAddress;
import java.time.LocalDateTime;
import java.util.HashMap;
import java.util.Map;

/**
 * Creating and retrieving Metadata
 */
public class CreatingMetadataApp
{
    private static String connectionString;

    private static BlobServiceClient blobServiceClient;

    private static BlobContainerClient containerClient;
    private static BlobContainerProperties containerProperties;

    private static BlobClient blobClient;

    private static String localPath;
    private static String fileName;

    public static void main(String[] args) throws IOException
    {
        BasicConfigurator.configure();
        Logger.getRootLogger().setLevel(Level.INFO);

        System.out.println("Creating and retrieving metadata\n");

        getConnectionString();

        connectToAzure();

        getContainers();

        uploadBlobsToContainers();

        listBlobsInContainer();
    }

    private static void getConnectionString()
    {
        connectionString = System.getenv("CONNECT_STR");

        System.out.printf("connectionString = %s%n%n", connectionString);
    }

    private static void connectToAzure()
    {
        // Create a BlobServiceClient object WITHOUT PROXY which will be used to create a container client
        blobServiceClient = new BlobServiceClientBuilder().connectionString(connectionString).buildClient();
    }

    private static void getContainers()
    {
        // Get a standard container and an immutable container
        containerClient = blobServiceClient.getBlobContainerClient("creating-metadata-1");
        containerProperties = containerClient.getProperties();

        System.out.printf("Container: %s - hasImmutabilityPolicy = %s%n", containerClient.getBlobContainerName(), containerProperties.hasImmutabilityPolicy());
    }

    private static void uploadBlobsToContainers() throws IOException
    {
        // Create a local file in the ./data/ directory for uploading and downloading
        localPath = "./data/";

        fileName = "creating-metadata-" + java.util.UUID.randomUUID() + ".txt";

        // Write text to the file
        try (FileWriter writer = new FileWriter(localPath + fileName, true))
        {
            writer.write("Testing metadata creation");
        }

        // Get a reference to a blob
        blobClient = containerClient.getBlobClient(fileName);

        System.out.println("\nUploading to Blob storage as blob:\n\t" + blobClient.getBlobUrl());

        Map<String, String> metadataMap = new HashMap<String, String>()
        {
            {
                put("dsCreatedTime", LocalDateTime.now().toString());
            }
        };

        // Upload the blob
        blobClient.uploadFromFile(localPath + fileName);
        blobClient.setMetadata(metadataMap);
    }

    private static void listBlobsInContainer()
    {
        System.out.printf("%nListing blobs for container %s%n", containerClient.getBlobContainerName());

        // List the blob(s) in the container.
        for (BlobItem blobItem : containerClient.listBlobs())
        {
            Map<String, String> metadata = blobItem.getMetadata();

            System.out.println("\t" + blobItem.getName() + "\tdsCreatedTime = " + metadata.get("dsCreatedTime"));
        }
    }
}

Expected behavior Since I have confirmed that the Metadata has been successfully saved, I should be able to retrieve it with the latest Blob SDK (v12).

Setup (please complete the following information):

gapra-msft commented 4 years ago

Hi @alberto-alvarado-m,

Thank you for your post.

By default, the listBlobs minimal overload does not send back the metadata. Metadata is one of the optional properties that can be returned back in a listBlobs call, so to retrieve metadata, I'd recommend you use the following code snippet to listBlobs.

ListBlobsOptions options = new ListBlobsOptions().setDetails(new BlobListDetails().setRetrieveMetadata(true));
containerClient.listBlobs(options, null);

Please let me know in case you run into any other issues.

MaRieck commented 3 years ago

Its resolved my issue as well, BUT the behaviour differs to the current Azurite (v3.11.0) Implementation.

Azurite automatically returns the metadata by default in listBlobs() which leads on our side to successful Junit-Tests whereas changing to the official Azure Cloud Blob Storage leads to NULL Metadata (solution is top ).

Please adapt Azurite accordingly, its not acceptable to have an offical Microsoft-Envrionment which behaviour is different from production environments.

gapra-msft commented 3 years ago

Hi @MaRieck Thank you for reporting this. I have transferred this issue to the Azurite repo where they will be able to help clarify the behavior of Azurite and take this up as a feature request if needed.

blueww commented 3 years ago

@alberto-alvarado-m, @MaRieck

Thanks for reporting this issue! We will evaluate it.

Azurite welcome contribution. It would be great if you could raise PR to fix this issue in Azurite!

stale[bot] commented 2 years ago

This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. Thank you for your contributions.