Home > database >  Return file from Google Cloud Storage through REST API without downloading locally first
Return file from Google Cloud Storage through REST API without downloading locally first

Time:01-10

We would like to have a Java REST API to return files from Google Cloud Storage as attachment. I was able to able to get it to work using the following method. The problem is that the file has to be downloaded locally to the service container (we are deploying on Google Cloud Run) and this is a problem in the case of very large files, and may generally be bad practice. Is there a way to modify this code somehow to skip the creation of a local file?

@GetMapping(path = "/file", produces = MediaType.APPLICATION_OCTET_STREAM_VALUE)
public ResponseEntity<InputStreamResource> getSpecificFile(@RequestParam String fileName,
        @RequestParam String bucketName, @RequestParam String projectName) {
    Storage storage = StorageOptions.newBuilder().setProjectId(projectId).build().getService();
    Blob blob = storage.get(bucketName, fileName);
    ReadChannel readChannel = blob.reader();
    String outputFileName = tempFileDestination.concat("\\").concat(fileName);
    try (FileOutputStream fileOutputStream = new FileOutputStream(outputFileName)) {
        fileOutputStream.getChannel().transferFrom(readChannel, 0, Long.MAX_VALUE);
        String contentType = Files.probeContentType(Paths.get(outputFileName));

        FileInputStream fileInputStream = new FileInputStream(outputFileName);
        return ResponseEntity.ok().contentType(MediaType.valueOf(contentType))
                .header("Content-Disposition", "attachment; filename="   fileName)
                .body(new InputStreamResource(fileInputStream));
    } catch (IOException e) {
        e.printStackTrace();
        return ResponseEntity.internalServerError().body(null);
    } finally {
        // delete the local file as cleanup
        try {
            Files.delete(Paths.get(outputFileName));
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

CodePudding user response:

Well, that did not take me long to figure out. I was able to make it work as follows:

@GetMapping(path = "/file", produces = MediaType.APPLICATION_OCTET_STREAM_VALUE)
public ResponseEntity<InputStreamResource> getSpecificFile(@RequestParam String fileName, @RequestParam String bucketName, @RequestParam String projectName) {
    Storage storage = StorageOptions.newBuilder().setProjectId(projectId).build().getService();
    Blob blob = storage.get(bucketName, fileName);
    ReadChannel readChannel = blob.reader();
    try {
        String contentType = Files.probeContentType(Paths.get(fileName));
        log.info("Determined the contentType for file {} to be: {}", fileName, contentType);

        InputStream inputStream = Channels.newInputStream(readChannel);
        return ResponseEntity.ok().contentType(MediaType.valueOf(contentType))
                .header("Content-Disposition", "attachment; filename="   fileName)
                .body(new InputStreamResource(inputStream));
    } catch (IOException e) {
        e.printStackTrace();
        return ResponseEntity.internalServerError().body(null);
    }
}

Basically redirect the InputStream to the readChannel instead of the file.

  • Related