Google Cloud Configuration With Spring Boot

Google Cloud Storage Spring Boot Integration

Google Cloud Storage Spring Boot integration can significantly enhance your project’s capabilities and your skills as a developer by enabling robust data storage and retrieval functionalities. In this article, I’ll walk you through the process of configuring Google Cloud with Spring Boot, covering authentication using Google Application Credentials and showing how to upload and download files to and from Google Cloud Storage. Before we move forward, I assume you have a basic understanding of Spring Boot, Java, and GCP.

Google cloud platform Setup

  1. Go to the Google Cloud Console.
  2. Create a new project or select an existing one.
  3. Enable the Google Cloud Storage JSON API.
  4. Navigate to IAM & Admin > Service Accounts.
  5. Select an existing one or create a new service account.
  6. Grant the service account the Storage Admin role.
  7. Generate a JSON key for the service account and download it.
  8. Set the GOOGLE_APPLICATION_CREDENTIALS environment variable to point to the JSON key file:
SSH Config
GOOGLE_APPLICATION_CREDENTIALS="/path/to/your-service-account-file.json"

Spring Boot Project Setup

  1. Go to Spring Initializr to generate a new Spring Boot project and import it into Intellij.
  2. Include the following dependencies:
    • Spring Web
    • Spring Boot DevTools
    • Google Cloud Storage
  3. In pom.xml, add the following dependency:
XML
<dependency>
    <groupId>com.google.cloud</groupId>
    <artifactId>google-cloud-storage</artifactId>
    <version>2.1.5</version>
</dependency>

Authentication Configuration

Create a class GoogleHttpRequestInitializer.java to handle authentication.

Java
import com.google.auth.http.HttpCredentialsAdapter;
import com.google.auth.oauth2.GoogleCredentials;
import com.google.api.client.http.HttpRequest;
import com.google.api.client.http.HttpRequestInitializer;

import java.io.IOException;
import java.util.Arrays;

public class GoogleHttpRequestInitializer implements HttpRequestInitializer {

    private GoogleCredentials credentials;
    private HttpCredentialsAdapter adapter;

    @Override
    public void initialize(HttpRequest httpRequest) throws IOException {
        credentials = GoogleCredentials.getApplicationDefault()
            .createScoped(Arrays.asList("https://www.googleapis.com/auth/cloud-platform"));
        adapter = new HttpCredentialsAdapter(credentials);

        adapter.initialize(httpRequest);
        httpRequest.setConnectTimeout(60000); // 1 minute connect timeout
        httpRequest.setReadTimeout(60000); // 1 minute read timeout
    }

    public GoogleCredentials getCredentials() {
        return credentials;
    }

    public void setCredentials(GoogleCredentials credentials) {
        this.credentials = credentials;
    }

    public HttpCredentialsAdapter getAdapter() {
        return adapter;
    }

    public void setAdapter(HttpCredentialsAdapter adapter) {
        this.adapter = adapter;
    }
}
  • GoogleCredentials.getApplicationDefault(): This method retrieves the default credentials from the environment variable GOOGLE_APPLICATION_CREDENTIALS.
  • createScoped(): It sets the scope for the credentials, allowing access to the Google Cloud Platform.
  • HttpCredentialsAdapter: This adapter integrates credentials with HTTP requests.

Storage Client Adapter Implementation

You need to create another class GoogleStorageClientAdapter.java to handle file operations.

  • uploadFile:
    • Creates a StorageObject with a file name and path.
    • Converts the MultipartFile to an InputStream.
    • Use storage.objects().insert to upload the file to Google Cloud Storage.
  • readFile:
    • Checks if the file exists using existFile.
    • Retrieves the StorageObject from Google Cloud Storage.
    • Builds a GET request to download the file using the object’s media link.
Java
import com.google.api.client.http.AbstractInputStreamContent;
import com.google.api.client.http.GenericUrl;
import com.google.api.client.http.InputStreamContent;
import com.google.api.services.storage.Storage;
import com.google.api.services.storage.model.StorageObject;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.web.multipart.MultipartFile;

import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;

@Component
@Slf4j
public class GoogleStorageClientAdapter {

    @Autowired
    private Storage storage;

    public Boolean uploadFile(String bucketName, String path, MultipartFile file) {
        log.info("GoogleStorageClientAdapter uploadFile with bucketName={} and path={}", bucketName, path);
        StorageObject object = new StorageObject();
        object.setName(path + file.getOriginalFilename());

        try (InputStream targetStream = new ByteArrayInputStream(file.getBytes())) {
            storage.objects().insert(bucketName, object,
                new AbstractInputStreamContent(path + file.getOriginalFilename()) {
                    @Override
                    public long getLength() {
                        return file.getSize();
                    }

                    @Override
                    public boolean retrySupported() {
                        return false;
                    }

                    @Override
                    public InputStream getInputStream() throws IOException {
                        return targetStream;
                    }
                }).execute();
        } catch (IOException e) {
            log.error("Exception GoogleStorageClientAdapter uploadFile: ", e);
            e.printStackTrace();
            return false;
        }

        log.info("GoogleStorageClientAdapter uploadFile done successfully");
        return true;
    }

    public InputStream readFile(String bucketName, String path, String fileName) {
        log.info("GoogleStorageClientAdapter readFile with bucketName={} path={} and fileName={}", bucketName, path, fileName);
        if (!existFile(bucketName, path, fileName)) {
            log.info("Cannot find details with bucketName={} path={} and fileName={}", bucketName, path, fileName);
            return null;
        }

        try {
            StorageObject object = storage.objects().get(bucketName, path + fileName).execute();
            return storage.getRequestFactory().buildGetRequest(new GenericUrl(object.getMediaLink()))
                .execute().getContent();
        } catch (IOException e) {
            log.error("Exception GoogleStorageClientAdapter readFile: ", e);
            e.printStackTrace();
            return null;
        }
    }

    private boolean existFile(String bucketName, String path, String fileName) {
        try {
            StorageObject object = storage.objects().get(bucketName, path + fileName).execute();
            return object != null;
        } catch (IOException e) {
            log.error("Exception in existFile: ", e);
            return false;
        }
    }
}

Storage Client Adapter in a Controller

Finally, one class FileController.java to manage file upload and download requests.

  • uploadFileToBucket:
    • Handle POST requests to /files/upload.
    • It calls uploadFile on the GoogleStorageClientAdapter.
    • Return a success or error response based on the outcome.
  • downloadFileFromBucket:
    • Handles GET requests to /files/download.
    • Calls readFile on the GoogleStorageClientAdapter.
    • Returns the file stream or a “file not found” message.
Java
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;

@RestController
@RequestMapping("/files")
public class FileController {

    @Autowired
    private GoogleStorageClientAdapter googleStorageClientAdapter;

    @PostMapping("/upload")
    public ResponseEntity<String> uploadFileToBucket(@RequestParam("file") MultipartFile file //file to be stored
    @RequestParam("path") String path, // bucket path
     @RequestParam("bucketName") String bucketName // bucket name) {
        boolean isUploaded = googleStorageClientAdapter.uploadFile(bucketName, path, file);
        if (isUploaded) {
            return new ResponseEntity<>("File uploaded successfully", HttpStatus.OK);
        } else {
            return new ResponseEntity<>("Failed to upload file", HttpStatus.INTERNAL_SERVER_ERROR);
        }
    }

    @GetMapping("/download")
    public ResponseEntity<?> downloadFileFromBucket() {
    // pass the bucket name, path, filename
        InputStream fileStream = googleStorageClientAdapter.readFile(bucketName, path, fileName);
        if (fileStream != null) {
            return ResponseEntity.ok().body(fileStream);
        }else {
            return new ResponseEntity<>("File not found", HttpStatus.NOT_FOUND);
        }
    }
}

Conclusion

Google Cloud Storage Spring Boot integration is a straight-forward process. Through configuring Google Application Credentials and utilizing the GoogleHttpRequestInitializer for authentication, you can seamlessly interact with Google Cloud Storage. Additionally, the GoogleStorageClientAdapter shows how to upload and read files from Google Cloud Storage, rendering it a valuable tool for your Spring Boot applications. Consequently, adhere to the above process to seamlessly integrate Google Cloud Storage into your Spring Boot project, thereby ensuring efficient and secure file management capabilities.

Leave a Comment

Comments

No comments yet. Why don’t you start the discussion?

Leave a Reply

Your email address will not be published. Required fields are marked *