티스토리 뷰

지난 포스팅에선 spring용 azure storage api의 스타터에 대해 살펴보았다.

1개의 blob 저장소만 사용한다면 매우 유용하겠지만, 안타깝게도 보통 1~2개정도의 blob은 쓰기 마련이다.

 

이제 starter를 제외한 azure java sdk로 구현을 해보자.

 

먼저 artifact는 2개가 필요하다. 

       <dependency>
            <groupId>com.azure</groupId>
            <artifactId>azure-storage-blob</artifactId>
            <version>12.25.2</version>
       </dependency>
       <dependency>
            <groupId>com.azure</groupId>
            <artifactId>azure-identity</artifactId>
            <version>1.11.4</version>
        </dependency>

 

azure-storage-blob 은 저장소를 다루기 위한 용도이고, azure-identity는 저장소에 업로드하기 위해 인증할 때 사용할 용도이다. (로그인에 connection string을 사용하는 경우엔 azure-identity가 필요 없다.)

azure sdk에서는 클라이언트를 크개 3개를 제공한다. 

BlobServiceClient

BlobContainerClient

BlobClient

 

아래와 같이 각 객체를 생성할 할 수 있다.

String containerName = "test"; // 추가해야함.
String blobName = "test/kimchi.txt"; // container 내 파일 경로 
var serviceClient = new BlobServiceClientBuilder().connectionString(connectionString).buildClient();
var blobContainerClient = serviceClient.getBlobContainerClient(containerName);
var blobClient = blobContainerClient.getBlobClient(blobName);

 

v11과 다르게 httpPipeline은 개발자가 추가하고 싶으면 추가하도록 변경하고 따로 설정하지 않으면 기본 설정을 세팅해준다. 그리고 더이상 외부에 해당 API가 Async 라고 홍보하지 않고 내부적으로 Async하게 조용히 처리하도록 구성을 변경했다. 

아래 구현은   BlobServiceClientBuilder 클래스 일부를 발췌한 것이다. buildClient를 호출해도, async client를 만들어준다. 

public BlobServiceClient buildClient() {
    return new BlobServiceClient(buildAsyncClient());
}

public BlobServiceAsyncClient buildAsyncClient() {
    BuilderHelper.httpsValidation(customerProvidedKey, "customer provided key", endpoint, LOGGER);

    boolean anonymousAccess = false;

    if (Objects.nonNull(customerProvidedKey) && Objects.nonNull(encryptionScope)) {
        throw LOGGER.logExceptionAsError(new IllegalArgumentException("Customer provided key and encryption "
            + "scope cannot both be set"));
    }

    BlobServiceVersion serviceVersion = version != null ? version : BlobServiceVersion.getLatest();
    HttpPipeline pipeline = (httpPipeline != null) ? httpPipeline : BuilderHelper.buildPipeline(
        storageSharedKeyCredential, tokenCredential, azureSasCredential, sasToken,
        endpoint, retryOptions, coreRetryOptions, logOptions,
        clientOptions, httpClient, perCallPolicies, perRetryPolicies, configuration, audience, LOGGER);

    boolean foundCredential = false;
    for (int i = 0; i < pipeline.getPolicyCount(); i++) {
        if (pipeline.getPolicy(i) instanceof StorageSharedKeyCredentialPolicy) {
            foundCredential = true;
            break;
        }
        if (pipeline.getPolicy(i) instanceof BearerTokenAuthenticationPolicy) {
            foundCredential = true;
            break;
        }
        if (pipeline.getPolicy(i) instanceof AzureSasCredentialPolicy) {
            foundCredential = true;
            break;
        }
    }
    anonymousAccess = !foundCredential;

    return new BlobServiceAsyncClient(pipeline, endpoint, serviceVersion, accountName, customerProvidedKey,
        encryptionScope, blobContainerEncryptionScope, anonymousAccess);
}

 

 

BlobClient 클래스에서 파일 업로드 하는 구현을 보면 명확하다. 내부적으로 async로 구현해서, 리턴을 받고 싶으면 Mono 로 리턴을 받고, 아니면 리턴을 하지 않도록 구현했다. 

@ServiceMethod(returns = ReturnType.SINGLE)
public void uploadFromFile(String filePath, ParallelTransferOptions parallelTransferOptions,
    BlobHttpHeaders headers, Map<String, String> metadata, AccessTier tier, BlobRequestConditions requestConditions,
    Duration timeout) {
    this.uploadFromFileWithResponse(new BlobUploadFromFileOptions(filePath)
        .setParallelTransferOptions(parallelTransferOptions).setHeaders(headers).setMetadata(metadata)
        .setTier(tier).setRequestConditions(requestConditions), timeout, null);
}
    @ServiceMethod(returns = ReturnType.SINGLE)
    public Response<BlockBlobItem> uploadFromFileWithResponse(BlobUploadFromFileOptions options, Duration timeout,
        Context context) {
        Mono<Response<BlockBlobItem>> upload =
            this.client.uploadFromFileWithResponse(options)
                .contextWrite(FluxUtil.toReactorContext(context));

        try {
            return StorageImplUtils.blockWithOptionalTimeout(upload, timeout);
        } catch (UncheckedIOException e) {
            throw LOGGER.logExceptionAsError(e);
        }
    }

 

connection string이 아닌 다른 인증방법을 사용하고 싶을 수 있다. 

일전에 restful API로 구현해놓은 것을 올렸었는데, 해당 방법은 인증토큰의 관리를 개발자가 직접해줬어야 했다. (주기적으로 데몬 스레드를 만들어 갱신해주거나, 매 요청마다 rest api로 토큰을 가져와야 했다.)

  azure-identity를 사용해 구현하면 인증토큰 관리는 API에서 알아서 한다. 

 

Azure service Principal을 이용하는 경우 아래처럼 인증 구현체를 추가해 넘겨주면 된다. 

        // 파라미터 값들은 사용자에 맞게 수정한다.
        String clientId = properties.getProperty("azure.blob.principal.clientId"); // secret 이름 
        String clientSecret = properties.getProperty("azure.blob.principal.clientSecret"); // secret 값
        String tenantId = properties.getProperty("azure.blob.principal.tenantId"); // azure tenant 값
        String endpoint = properties.getProperty("azure.blob.endpoint"); // blob 주소
        ClientSecretCredential clientSecretCredential = new ClientSecretCredentialBuilder()
                .clientId(clientId)
                .clientSecret(clientSecret)
                .tenantId(tenantId)
                .build();
                
        var client = new BlobServiceClientBuilder()
            .endpoint(endpoint)
            .credential(clientSecretCredential)
            .buildClient();

 

azure 의 인증방식은 여러개가 있는데, 다음 포스팅에서 keyvault에 대해 다루면서 같이 다루도록 하겠다. 

공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
«   2025/01   »
1 2 3 4
5 6 7 8 9 10 11
12 13 14 15 16 17 18
19 20 21 22 23 24 25
26 27 28 29 30 31
글 보관함