From a9615a5c00e7295f263b5b1c70826caa59f40185 Mon Sep 17 00:00:00 2001 From: Ali Oezyetimoglu Date: Fri, 14 Jul 2023 15:03:55 +0200 Subject: [PATCH] RED-7140: changed to CompletableFuture with InputStreamResource to make it possible to download files greater than 5GB --- .../impl/controller/DownloadController.java | 41 ++++++++++--------- .../external/resource/DownloadResource.java | 9 ++-- 2 files changed, 28 insertions(+), 22 deletions(-) diff --git a/persistence-service-v1/persistence-service-external-api-impl-v1/src/main/java/com/iqser/red/persistence/service/v1/external/api/impl/controller/DownloadController.java b/persistence-service-v1/persistence-service-external-api-impl-v1/src/main/java/com/iqser/red/persistence/service/v1/external/api/impl/controller/DownloadController.java index 0a43bea64..903e2ba9a 100644 --- a/persistence-service-v1/persistence-service-external-api-impl-v1/src/main/java/com/iqser/red/persistence/service/v1/external/api/impl/controller/DownloadController.java +++ b/persistence-service-v1/persistence-service-external-api-impl-v1/src/main/java/com/iqser/red/persistence/service/v1/external/api/impl/controller/DownloadController.java @@ -7,6 +7,7 @@ import static com.iqser.red.service.persistence.management.v1.processor.utils.Do import java.util.List; import java.util.Map; import java.util.Optional; +import java.util.concurrent.CompletableFuture; import java.util.regex.Pattern; import java.util.stream.Collectors; @@ -196,15 +197,16 @@ public class DownloadController implements DownloadResource { @PreAuthorize("hasAuthority('" + PROCESS_DOWNLOAD + "')") - public ResponseEntity downloadFile(@RequestParam(STORAGE_ID) String storageId, - @RequestParam(value = "inline", required = false, defaultValue = FALSE) boolean inline) { + public CompletableFuture> downloadFile(@RequestParam(STORAGE_ID) String storageId, + @RequestParam(value = "inline", required = false, defaultValue = FALSE) boolean inline) { + return CompletableFuture.supplyAsync(() -> { + var userId = KeycloakSecurity.getUserId(); - var userId = KeycloakSecurity.getUserId(); + var downloadStatus = getDownloadStatus(storageId, userId); + var fileDownloadStream = getFileForDownload(storageId, userId); - var downloadStatus = getDownloadStatus(storageId, userId); - var fileDownloadStream = getFileForDownload(storageId, userId); - - return getResponseEntity(inline, fileDownloadStream, downloadStatus.getFilename(), MediaType.parseMediaType("application/zip")); + return getResponseEntity(inline, fileDownloadStream, downloadStatus.getFilename(), MediaType.parseMediaType("application/zip"), downloadStatus.getFileSize()); + }); } @@ -238,15 +240,16 @@ public class DownloadController implements DownloadResource { @SneakyThrows - private ResponseEntity getResponseEntity(boolean inline, InputStreamResource resource, String filename, MediaType mediaType) { + private ResponseEntity getResponseEntity(boolean inline, InputStreamResource resource, String filename, MediaType mediaType, long fileSize) { HttpHeaders httpHeaders = new HttpHeaders(); httpHeaders.setContentType(mediaType); + httpHeaders.setContentLength(fileSize); if (filename != null) { httpHeaders.add("Content-Disposition", inline ? "inline" : "attachment" + "; filename*=utf-8''" + StringEncodingUtils.urlEncode(filename)); } - return new ResponseEntity<>(fileProxyStreamForDownload(resource.getInputStream()), httpHeaders, HttpStatus.OK); + return new ResponseEntity<>(resource, httpHeaders, HttpStatus.OK); } @@ -261,21 +264,21 @@ public class DownloadController implements DownloadResource { @Override - public ResponseEntity downloadFileUsingOTT(@PathVariable(OTT) String oneTimeToken, + public CompletableFuture> downloadFileUsingOTT(@PathVariable(OTT) String oneTimeToken, @RequestParam(value = "inline", required = false, defaultValue = FALSE) boolean inline, @RequestParam(value = "tenantId") String tenantId) { + return CompletableFuture.supplyAsync(() -> { + TenantContext.setTenantId(tenantId); - TenantContext.setTenantId(tenantId); + log.debug("downloadFileUsingOTT '{}'", oneTimeToken); + var token = oneTimeTokenDownloadService.getToken(oneTimeToken); + var downloadStatus = getDownloadStatus(token.getStorageId(), token.getUserId()); + var fileDownloadStream = getFileForDownload(token.getStorageId(), token.getUserId()); - log.debug("downloadFileUsingOTT '{}'", oneTimeToken); - var token = oneTimeTokenDownloadService.getToken(oneTimeToken); - var downloadStatus = getDownloadStatus(token.getStorageId(), token.getUserId()); - var fileDownloadStream = getFileForDownload(token.getStorageId(), token.getUserId()); - - TenantContext.clear(); - - return getResponseEntity(inline, fileDownloadStream, downloadStatus.getFilename(), MediaType.parseMediaType("application/zip")); + TenantContext.clear(); + return getResponseEntity(inline, fileDownloadStream, downloadStatus.getFilename(), MediaType.parseMediaType("application/zip"), downloadStatus.getFileSize()); + }); } diff --git a/persistence-service-v1/persistence-service-external-api-v1/src/main/java/com/iqser/red/service/persistence/service/v1/api/external/resource/DownloadResource.java b/persistence-service-v1/persistence-service-external-api-v1/src/main/java/com/iqser/red/service/persistence/service/v1/api/external/resource/DownloadResource.java index 056d7085d..ee76db26c 100644 --- a/persistence-service-v1/persistence-service-external-api-v1/src/main/java/com/iqser/red/service/persistence/service/v1/api/external/resource/DownloadResource.java +++ b/persistence-service-v1/persistence-service-external-api-v1/src/main/java/com/iqser/red/service/persistence/service/v1/api/external/resource/DownloadResource.java @@ -1,6 +1,9 @@ package com.iqser.red.service.persistence.service.v1.api.external.resource; +import java.util.concurrent.CompletableFuture; + import org.springframework.core.io.FileSystemResource; +import org.springframework.core.io.InputStreamResource; import org.springframework.http.HttpStatus; import org.springframework.http.MediaType; import org.springframework.http.ResponseEntity; @@ -66,8 +69,8 @@ public interface DownloadResource { @Operation(summary = "Returns a downloadable byte stream of the requested file", description = "Use the optional \"inline\" request parameter " + "to select, if this report will be opened in the browser.") @ApiResponses(value = {@ApiResponse(responseCode = "200", description = "OK"), @ApiResponse(responseCode = "404", description = "Download with this Id is no longer available")}) @GetMapping(value = REST_PATH) - ResponseEntity downloadFile(@RequestParam(STORAGE_ID) String storageId, - @RequestParam(value = "inline", required = false, defaultValue = FALSE) boolean inline); + CompletableFuture> downloadFile(@RequestParam(STORAGE_ID) String storageId, + @RequestParam(value = "inline", required = false, defaultValue = FALSE) boolean inline); @ResponseBody @@ -83,7 +86,7 @@ public interface DownloadResource { @Operation(summary = "Returns a downloadable byte stream of the requested file using a valid oneTimeToken", description = "Use the optional \"inline\" request parameter " + "to select, if this report will be opened in the browser.") @ApiResponses(value = {@ApiResponse(responseCode = "200", description = "OK"), @ApiResponse(responseCode = "404", description = "Download with this Id is no longer available"), @ApiResponse(responseCode = "400", description = "OTT is not valid")}) @GetMapping(value = REST_PATH + OTT_PATH + OTT_PATH_VARIABLE) - ResponseEntity downloadFileUsingOTT(@PathVariable(OTT) String oneTimeToken, + CompletableFuture> downloadFileUsingOTT(@PathVariable(OTT) String oneTimeToken, @RequestParam(value = "inline", required = false, defaultValue = FALSE) boolean inline, @RequestParam(value = "tenantId") String tenantId); -- 2.47.2