Home > Net >  Get file from GCS without downloading it locally
Get file from GCS without downloading it locally

Time:06-04

I have a simple Spring Boot microservice that takes care of uploading, retrieving and deleting images to/from Google Cloud Storage. I have the following code for the get request in my service:

public StorageObject getImage(String fileName) throws IOException {
    StorageObject object = storage.objects().get(bucketName, fileName).execute();
    File file = new File("./"   fileName);
    FileOutputStream os = new FileOutputStream(file);

    storage.getRequestFactory()
            .buildGetRequest(new GenericUrl(object.getMediaLink()))
            .execute()
            .download(os);
    object.set("file", file);
    return object;
}

And this is my controller part:

@GetMapping("/get/image/{id}")
public ResponseEntity<byte[]> getImage(@PathVariable("id") Long id) {
    try {
        String fileName = imageService.findImageById(id);
        StorageObject object = gcsService.getImage(fileName);

        byte[] res = Files.toByteArray((File) object.get("file"));

        return ResponseEntity.ok()
                .contentType(MediaType.IMAGE_JPEG)
                .body(res);
    } catch (IOException e) {
        e.printStackTrace();
        throw new RuntimeException("No such file or directory");
    }
}

It all works fine in terms of getting the image in the response, but my problem is that the images get downloaded at the root directory of the project too. Many images are going to be uploaded through this service so this is an issue. I only want to display the images in the response (as a byteArray), without having them download. I tried playing with the code but couldn't manage to get it to work as I want.

CodePudding user response:

I'd suggest to instead stream the download, while skipping the FileChannel operation:

public static void streamObjectDownload(
    String projectId, String bucketName, String objectName, String targetFile
) {

    Storage storage = StorageOptions.newBuilder().setProjectId(projectId).build().getService();
    try (ReadChannel reader = storage.reader(BlobId.of(bucketName, objectName));
        
      FileChannel targetFileChannel = FileChannel.open(Paths.get(targetFile), StandardOpenOption.WRITE)) {

          ByteStreams.copy(reader, targetFileChannel);
          System.out.println(
              "Downloaded object "   objectName
                    " from bucket "   bucketName
                    " to "   targetFile
                    " using a ReadChannel.");
        }

    } catch (IOException e) {
        e.printStacktrace()
    }
}

One can eg. obtain a FileChannel from a RandomAccessFile:

RandomAccessFile file = new RandomAccessFile(Paths.get(targetFile), StandardOpenOption.WRITE);
FileChannel channel = file.getChannel();

While the Spring framework similarly has a GoogleStorageResource:

public OutputStream getOutputStream() throws IOException

Returns the output stream for a Google Cloud Storage file.

Then convert from OutputStream to int[] (this may be binary or ASCII data):

int[] bytes = os.toByteArray();

CodePudding user response:

Would it work for you to create Signed URLs in Cloud Storage to display your images? These URLs give access to storage bucket files for a limited time, and then expire, so you would rather not store temporary copies of the image locally as is suggested in this post.

  • Related