SaFi Bank Space : Google Cloud Storage on Micronaut (Kotlin)

Cloud Storage is a service for storing your objects in Google Cloud. An object is an immutable piece of data consisting of a file of any format. You store objects in containers called buckets. All buckets are associated with a project, and you can group your projects under an organization.

To implement GCS (Google Cloud Storage) in Micronaut we can use the guide from google itself in this link. Previously we try to use Micronaut GCP integration to wrap the credential but it didn’t work.

Here are the steps to implement GCS using google cloud client library:

  • Install client library (Gradle)

implementation(platform("com.google.cloud:libraries-bom:25.3.0"))
implementation("com.google.cloud:google-cloud-storage")
  • Prepare GCP credential (In this case using service account, download the json key and store it)

  • To use the service account json file for GCS credential, simply add the credential file path to environment variable GOOGLE_APPLICATION_CREDENTIALS.

export GOOGLE_APPLICATION_CREDENTIALS="/home/user/Downloads/service-account-file.json"
  • Then we can use the client library and the credential will automatically resolved.

  • This code example below will show the attempt to upload an image to GCS bucket test-safi-dev and also get signed URL.

package com.safi.cardmanager.service.impl

import com.google.cloud.storage.BlobId
import com.google.cloud.storage.BlobInfo
import com.google.cloud.storage.Storage
import com.google.cloud.storage.StorageOptions
import com.safi.cardmanager.service.GoogleService
import jakarta.inject.Singleton
import java.net.URL
import java.util.concurrent.TimeUnit

@Singleton
open class GoogleServiceImpl : GoogleService {
    private lateinit var storageService: Storage

    private fun getStorageService(): Storage {
        if (!::storageService.isInitialized) {
            val projectId = "acquired-badge-348405"

            storageService =
                StorageOptions.newBuilder().setProjectId(projectId).build().service
        }

        return storageService
    }

    override fun uploadImage(filename: String, fileData: ByteArray) {
        val bucketName = "test-safi-dev"

        val storage = getStorageService()

        val blobId = BlobId.of(bucketName, filename)
        val blobInfo = BlobInfo.newBuilder(blobId).build()

        storage.create(blobInfo, fileData)

        println("File $filename uploaded to bucket $bucketName as $filename")
    }

    override fun getImageSignedUrl(filename: String): URL {
        val bucketName = "test-safi-dev"

        val storage = getStorageService()

        val blobId = BlobId.of(bucketName, filename)
        val blobInfo = BlobInfo.newBuilder(blobId).build()

        val url = storage.signUrl(blobInfo, 15, TimeUnit.MINUTES, Storage.SignUrlOption.withV4Signature())

        println("Generated GET signed URL:");
        println(url);
        println("You can use this URL with any user agent, for example:");
        println("curl '$url'");

        return url
    }
}