RED-5205 - direct stream download implementation #122
@ -3,6 +3,7 @@ package com.iqser.red.persistence.service.v1.external.api.impl.controller;
|
||||
import static com.iqser.red.service.persistence.management.v1.processor.roles.ActionRoles.PROCESS_DOWNLOAD;
|
||||
import static com.iqser.red.service.persistence.management.v1.processor.roles.ActionRoles.READ_DOWNLOAD_STATUS;
|
||||
|
||||
import java.io.BufferedInputStream;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
@ -22,6 +23,9 @@ import org.springframework.web.bind.annotation.PathVariable;
|
||||
import org.springframework.web.bind.annotation.RequestBody;
|
||||
import org.springframework.web.bind.annotation.RequestParam;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
import org.springframework.web.context.request.RequestAttributes;
|
||||
import org.springframework.web.context.request.RequestContextHolder;
|
||||
import org.springframework.web.context.request.ServletRequestAttributes;
|
||||
|
||||
import com.iqser.red.persistence.service.v1.external.api.impl.service.OneTimeTokenService;
|
||||
import com.iqser.red.service.persistence.management.v1.processor.exception.BadRequestException;
|
||||
@ -52,6 +56,7 @@ import com.iqser.red.storage.commons.service.StorageService;
|
||||
import com.knecon.fforesight.keycloakcommons.security.KeycloakSecurity;
|
||||
import com.knecon.fforesight.tenantcommons.TenantContext;
|
||||
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.SneakyThrows;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
@ -154,7 +159,7 @@ public class DownloadController implements DownloadResource {
|
||||
// special corner case: unapproved files, no reports and only REDACTED type selected
|
||||
if (approvedFiles.isEmpty() && (request.getReportTemplateIds() == null || request.getReportTemplateIds()
|
||||
.isEmpty()) && request.getDownloadFileTypes() != null && request.getDownloadFileTypes().size() == 1 && request.getDownloadFileTypes()
|
||||
.contains(DownloadFileType.REDACTED)) {
|
||||
.contains(DownloadFileType.REDACTED)) {
|
||||
throw new BadRequestException("Unapproved files in redacted state with no reports cannot be included");
|
||||
}
|
||||
}
|
||||
@ -211,20 +216,23 @@ public class DownloadController implements DownloadResource {
|
||||
}
|
||||
|
||||
|
||||
@SneakyThrows
|
||||
@PreAuthorize("hasAuthority('" + PROCESS_DOWNLOAD + "')")
|
||||
public CompletableFuture<ResponseEntity<InputStreamResource>> downloadFile(@RequestParam(STORAGE_ID) String storageId,
|
||||
@RequestParam(value = "inline", required = false, defaultValue = FALSE) boolean inline) {
|
||||
public void downloadFile(@RequestParam(STORAGE_ID) String storageId) {
|
||||
|
||||
var requestAttributes = RequestContextHolder.getRequestAttributes();
|
||||
HttpServletResponse response = ((ServletRequestAttributes) requestAttributes).getResponse();
|
||||
|
||||
var userId = KeycloakSecurity.getUserId();
|
||||
var tenantId = TenantContext.getTenantId();
|
||||
var downloadStatus = getDownloadStatus(storageId, userId);
|
||||
var fileDownloadStream = getFileForDownload(storageId, userId);
|
||||
|
||||
return CompletableFuture.supplyAsync(() -> {
|
||||
response.setContentType("application/zip");
|
||||
response.setHeader("Content-Disposition", "attachment" + "; filename*=utf-8''" + StringEncodingUtils.urlEncode(downloadStatus.getFilename()));
|
||||
response.setHeader("Content-Length", String.valueOf(downloadStatus.getFileSize()));
|
||||
|
||||
TenantContext.setTenantId(tenantId);
|
||||
var downloadStatus = getDownloadStatus(storageId, userId);
|
||||
var fileDownloadStream = getFileForDownload(storageId, userId);
|
||||
|
||||
return getResponseEntity(inline, fileDownloadStream, downloadStatus.getFilename(), MediaType.parseMediaType("application/zip"), downloadStatus.getFileSize());
|
||||
});
|
||||
org.apache.commons.io.IOUtils.copyLarge(fileDownloadStream.getInputStream(), response.getOutputStream());
|
||||
response.flushBuffer();
|
||||
}
|
||||
|
||||
|
||||
@ -250,28 +258,13 @@ public class DownloadController implements DownloadResource {
|
||||
.build());
|
||||
downloadService.setDownloaded(JSONPrimitive.of(storageId));
|
||||
|
||||
return response;
|
||||
return new InputStreamResource(new BufferedInputStream(response.getInputStream()));
|
||||
} catch (Exception e) {
|
||||
throw new NotFoundException(e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@SneakyThrows
|
||||
private ResponseEntity<InputStreamResource> 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<>(resource, httpHeaders, HttpStatus.OK);
|
||||
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
@PreAuthorize("hasAuthority('" + PROCESS_DOWNLOAD + "')")
|
||||
public JSONPrimitive<String> generateOneTimeToken(@RequestBody JSONPrimitive<String> storageIdWrapper) {
|
||||
@ -282,21 +275,24 @@ public class DownloadController implements DownloadResource {
|
||||
|
||||
|
||||
@Override
|
||||
public CompletableFuture<ResponseEntity<InputStreamResource>> 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);
|
||||
@SneakyThrows
|
||||
public void downloadFileUsingOTT(@PathVariable(OTT) String oneTimeToken,
|
||||
@RequestParam(value = "tenantId") String tenantId) {
|
||||
|
||||
log.debug("downloadFileUsingOTT {}", oneTimeToken);
|
||||
var token = oneTimeTokenDownloadService.getToken(oneTimeToken);
|
||||
var downloadStatus = getDownloadStatus(token.getStorageId(), token.getUserId());
|
||||
var fileDownloadStream = getFileForDownload(token.getStorageId(), token.getUserId());
|
||||
TenantContext.setTenantId(tenantId);
|
||||
var token = oneTimeTokenDownloadService.getToken(oneTimeToken);
|
||||
var requestAttributes = RequestContextHolder.getRequestAttributes();
|
||||
HttpServletResponse response = ((ServletRequestAttributes) requestAttributes).getResponse();
|
||||
|
||||
TenantContext.clear();
|
||||
var downloadStatus = getDownloadStatus(token.getStorageId(), token.getUserId());
|
||||
var fileDownloadStream = getFileForDownload(token.getStorageId(), token.getUserId());
|
||||
|
||||
return getResponseEntity(inline, fileDownloadStream, downloadStatus.getFilename(), MediaType.parseMediaType("application/zip"), downloadStatus.getFileSize());
|
||||
});
|
||||
response.setContentType("application/zip");
|
||||
response.setHeader("Content-Disposition", "attachment" + "; filename*=utf-8''" + StringEncodingUtils.urlEncode(downloadStatus.getFilename()));
|
||||
response.setHeader("Content-Length", String.valueOf(downloadStatus.getFileSize()));
|
||||
|
||||
org.apache.commons.io.IOUtils.copyLarge(fileDownloadStream.getInputStream(), response.getOutputStream());
|
||||
response.flushBuffer();
|
||||
}
|
||||
|
||||
|
||||
@ -305,11 +301,13 @@ public class DownloadController implements DownloadResource {
|
||||
return DownloadRequest.builder().dossierId(request.getDossierId()).userId(KeycloakSecurity.getUserId()).fileIds(request.getFileIds()).build();
|
||||
}
|
||||
|
||||
|
||||
private String generateReportJsonStorageIdForS3(String storageId) {
|
||||
|
||||
return storageId.substring(0, storageId.length() - 3) + REPORT_INFO;
|
||||
}
|
||||
|
||||
|
||||
private String generateReportJsonStorageIdForAzure(String storageId) {
|
||||
|
||||
return storageId.substring(0, storageId.length() - 4) + REPORT_INFO;
|
||||
|
||||
@ -66,11 +66,11 @@ public interface DownloadResource {
|
||||
|
||||
@ResponseBody
|
||||
@ResponseStatus(value = HttpStatus.OK)
|
||||
@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.")
|
||||
@Operation(summary = "Returns a downloadable byte stream of the requested file")
|
||||
@ApiResponses(value = {@ApiResponse(responseCode = "200", description = "OK"), @ApiResponse(responseCode = "404", description = "Download with this Id is no longer available")})
|
||||
@GetMapping(value = REST_PATH)
|
||||
CompletableFuture<ResponseEntity<InputStreamResource>> downloadFile(@RequestParam(STORAGE_ID) String storageId,
|
||||
@RequestParam(value = "inline", required = false, defaultValue = FALSE) boolean inline);
|
||||
@GetMapping(value = REST_PATH+"/primitive")
|
||||
void downloadFile(@RequestParam(STORAGE_ID) String storageId);
|
||||
|
||||
|
||||
|
||||
@ResponseBody
|
||||
@ -83,11 +83,9 @@ public interface DownloadResource {
|
||||
|
||||
@ResponseBody
|
||||
@ResponseStatus(value = HttpStatus.OK)
|
||||
@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.")
|
||||
@Operation(summary = "Returns a downloadable byte stream of the requested file using a valid oneTimeToken")
|
||||
@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)
|
||||
CompletableFuture<ResponseEntity<InputStreamResource>> downloadFileUsingOTT(@PathVariable(OTT) String oneTimeToken,
|
||||
@RequestParam(value = "inline", required = false, defaultValue = FALSE) boolean inline,
|
||||
@RequestParam(value = "tenantId") String tenantId);
|
||||
void downloadFileUsingOTT(@PathVariable(OTT) String oneTimeToken, @RequestParam(value = "tenantId") String tenantId);
|
||||
|
||||
}
|
||||
|
||||
@ -1,5 +1,6 @@
|
||||
package com.iqser.red.service.persistence.management.v1.processor.service;
|
||||
|
||||
import java.io.BufferedInputStream;
|
||||
import java.io.File;
|
||||
import java.io.InputStream;
|
||||
import java.nio.file.Files;
|
||||
@ -60,7 +61,7 @@ public class FileManagementStorageService {
|
||||
|
||||
File tempFile = File.createTempFile("temp", ".data");
|
||||
storageService.downloadTo(tenantId, storageId, tempFile);
|
||||
return Files.newInputStream(Paths.get(tempFile.getPath()), StandardOpenOption.DELETE_ON_CLOSE);
|
||||
return new BufferedInputStream(Files.newInputStream(Paths.get(tempFile.getPath()), StandardOpenOption.DELETE_ON_CLOSE));
|
||||
}
|
||||
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user