Azure SDK for Javaを使ってAzure Storage Blobにアクセスする

はじめに

Azure Storage Blobに対してJavaプログラムからアクセスする方法についてちょっと調査をしていたら色々とハマったので、備忘録として記します。

Azure SDK for Java

Azure SDK for Javaのドキュメントは以下にあります。

docs.microsoft.com

Azure Storage Blobに対するJavaプログラムからのアクセスについては以下にサンプルがあります。

docs.microsoft.com

Azure SDK for Javaにはv8系とv12系の二つがあるようです。Javaのバージョンとはあまり関係がない?みたいで、今はv12系を使うようです。

ここに書いてあることで多くのことを満たせるかなと思います。以下では実際に調査をしてハマったことを記します。

BOMを使う

Azure SDK for Javaではさまざまなライブラリが提供されているのですが、それらの依存関係やバージョンを統一するためにBOMが提供されています。

devblogs.microsoft.com

Gradleでは5.2からBOMをサポートしているようです。

docs.gradle.org

build.gradle上で以下のように書けばOKです。

dependencies {
    implementation platform('com.azure:azure-sdk-bom:1.2.0')
    implementation 'com.azure:azure-storage-blob'
}

BOMを使うとv12系が利用されるようでした。

ログを出力する

実行した際、以下のメッセージが出力されることがあります。

SLF4J: Failed to load class "org.slf4j.impl.StaticLoggerBinder".
SLF4J: Defaulting to no-operation (NOP) logger implementation
SLF4J: See http://www.slf4j.org/codes.html#StaticLoggerBinder for further details.

これはLogbackなどのロギングライブラリを導入しておけば出力されません。Logbackを使う場合、Gradleに以下の記述をします。

dependencies {
    implementation platform('com.azure:azure-sdk-bom:1.2.0')
    implementation 'com.azure:azure-storage-blob'
    implementation 'ch.qos.logback:logback-classic:1.2.11'  // ← 追加
}

Logbackの設定をsrc/main/resources/logback.xmlに書きます。内容は以下のような感じ。

<configuration>
    <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
        <encoder>
            <pattern>
                %d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n
            </pattern>
        </encoder>
    </appender>
    <root level="info">
        <appender-ref ref="STDOUT" />
    </root>
</configuration>

このようにすることで、以下のようなログが出力されました。

2022-03-27 15:38:38.603 [main] INFO  c.a.c.i.jackson.JacksonVersion - Package versions: jackson-annotations=2.13.1, jackson-core=2.13.1, jackson-databind=2.13.1, jackson-dataformat-xml=2.13.1, jackson-datatype-jsr310=2.13.1, azure-core=1.26.0, Troubleshooting version conflicts: https://aka.ms/azsdk/java/dependency/troubleshoot

詳細は以下にも書かれています。

docs.microsoft.com

JavaDoc

細かいメソッドなどを調べるためにJavaDocは欠かせません。以下のページにてAzure SDK for Javaとして提供しているサービスごとのJavaDocのリンクがまとまっています。

azure.github.io

Azure Storage Blobは以下のページから辿れるJavaDocを参照すると良いかと思います。

azure.github.io

CONTENT-TYPEなどを設定する

上記のサンプルをもとに実行するとCONTENT-TYPEapplication/octet-streamになっていました。

これを修正するためには色々とコードを書く必要があります。

blobClientの型をBlobClientからBlockBlobClientにします。

BlockBlobClient blobClient = blobContainerClient.getBlobClient(blobName).getBlockBlobClient();

CONTENT-TYPEを設定するにはBlobHttpHeadersインスタンスを作成します。

BlobHttpHeaders blobHttpHeaders = new BlobHttpHeaders().setContentDisposition("attachment").setContentType("text/plain");

あとはBlockBlobClientuploadWithResponseメソッドを使ってアップロードします。

以下のような実装になりました(package文やimport文は省略)。

public class BlobHelper {
    private BlobServiceClient blobServiceClient;
    private BlobContainerClient blobContainerClient;

    public BlobHelper() {
    }

    public void createBlobServiceClient(String connectionString) {
        // blobに接続するためのクライアントを作成する
        this.blobServiceClient = new BlobServiceClientBuilder()
                .connectionString(connectionString)
                .buildClient();
    }

    public void createBlobContainerClient(String containerName) {
        // コンテナーにアクセスするクライアントを作成する
        this.blobContainerClient = this.blobServiceClient
                .getBlobContainerClient(containerName);
        if (!this.blobContainerClient.exists()) {
            this.blobContainerClient.create();
        }
    }

    void createBlobWithData(String blobName, String blobContents) throws NoSuchAlgorithmException {
        // Blobを作成する
        BlockBlobClient blobClient = blobContainerClient.getBlobClient(blobName).getBlockBlobClient();
        // メタデータの設定
        Map<String, String> blobMetadata = Collections.singletonMap("myblobmetadata", "sample");
        // 各種ヘッダーの設定
        BlobHttpHeaders blobHttpHeaders = new BlobHttpHeaders().setContentDisposition("attachment")
                .setContentType("text/plain");
        byte[] md5 = MessageDigest.getInstance("MD5").digest(blobContents.getBytes(StandardCharsets.UTF_8));
        InputStream dataStream = new ByteArrayInputStream(blobContents.getBytes(StandardCharsets.UTF_8));
        blobClient.uploadWithResponse(dataStream, blobContents.length(), blobHttpHeaders, blobMetadata, null, md5, null, null, null);
    }
}

以下のような結果となりました。無事、CONTENT-TYPEtext/plainになっています。他にも各種メタデータも設定されています。

ソース

この時点のソースは以下です。

github.com