From decb5a1af537d533d8ecf8795ad6cbb25860fd2d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Johnny=20Miller=20=28=E9=94=BA=E4=BF=8A=29?= Date: Thu, 12 Aug 2021 12:00:01 +0800 Subject: [PATCH] perf($OSS): fully support async stream and download resources fully support async stream and download resources BREAKING CHANGE: fully support async stream and download resources --- .../controller/ReadResourceController.java | 13 +-- .../read/service/ReadResourceService.java | 29 +++++-- .../service/impl/ReadResourceServiceImpl.java | 82 +++++++++++++------ 3 files changed, 83 insertions(+), 41 deletions(-) diff --git a/oss-center/src/main/java/com/jmsoftware/maf/osscenter/read/controller/ReadResourceController.java b/oss-center/src/main/java/com/jmsoftware/maf/osscenter/read/controller/ReadResourceController.java index f3a9c74e..3d54bdd9 100644 --- a/oss-center/src/main/java/com/jmsoftware/maf/osscenter/read/controller/ReadResourceController.java +++ b/oss-center/src/main/java/com/jmsoftware/maf/osscenter/read/controller/ReadResourceController.java @@ -4,7 +4,6 @@ import io.swagger.annotations.Api; import io.swagger.annotations.ApiOperation; import lombok.RequiredArgsConstructor; -import org.springframework.core.io.Resource; import org.springframework.http.HttpHeaders; import org.springframework.http.ResponseEntity; import org.springframework.validation.annotation.Validated; @@ -30,13 +29,6 @@ public class ReadResourceController { @GetMapping("/stream/{bucket}/{object}") @ApiOperation(value = "Stream single resource", notes = "Stream single resource") - public ResponseEntity streamSingleResource(@PathVariable String bucket, @PathVariable String object, - @RequestHeader(name = HttpHeaders.RANGE, required = false) String range) { - return this.readResourceService.streamSingleResource(bucket, object, range); - } - - @GetMapping("/async/stream/{bucket}/{object}") - @ApiOperation(value = "Stream single resource", notes = "Stream single resource") public ResponseEntity asyncStreamSingleResource(@PathVariable String bucket, @PathVariable String object, @RequestHeader(name = HttpHeaders.RANGE, @@ -46,7 +38,8 @@ public ResponseEntity asyncStreamSingleResource(@PathVari @GetMapping("/download/{bucket}/{object}") @ApiOperation(value = "Download single resource", notes = "Download single resource") - public ResponseEntity downloadSingleResource(@PathVariable String bucket, @PathVariable String object) { - return this.readResourceService.downloadSingleResource(bucket, object); + public ResponseEntity downloadSingleResource(@PathVariable String bucket, + @PathVariable String object) { + return this.readResourceService.asyncDownloadSingleResource(bucket, object); } } diff --git a/oss-center/src/main/java/com/jmsoftware/maf/osscenter/read/service/ReadResourceService.java b/oss-center/src/main/java/com/jmsoftware/maf/osscenter/read/service/ReadResourceService.java index 5aa4a76b..c6c22983 100644 --- a/oss-center/src/main/java/com/jmsoftware/maf/osscenter/read/service/ReadResourceService.java +++ b/oss-center/src/main/java/com/jmsoftware/maf/osscenter/read/service/ReadResourceService.java @@ -24,26 +24,37 @@ public interface ReadResourceService { DataSize LARGE_CHUNK_SIZE = DataSize.ofMegabytes(8); /** - * Stream single resource. + * Async stream single resource response entity. * * @param bucket the bucket * @param object the object * @param range the range - * @return the single resource + * @return the response entity */ - ResponseEntity streamSingleResource(@NotBlank String bucket, @NotBlank String object, - @Nullable String range); + ResponseEntity asyncStreamSingleResource(@NotBlank String bucket, @NotBlank String object, + @Nullable String range); /** - * Async stream single resource response entity. + * Async download single resource response entity. * * @param bucket the bucket * @param object the object - * @param range the range * @return the response entity */ - ResponseEntity asyncStreamSingleResource(@NotBlank String bucket, @NotBlank String object, - @Nullable String range); + ResponseEntity asyncDownloadSingleResource(@NotBlank String bucket, @NotBlank String object); + + /** + * Stream single resource. + * + * @param bucket the bucket + * @param object the object + * @param range the range + * @return the single resource + * @deprecated since it's not async method, will be deleted in the future. + */ + @Deprecated(forRemoval = true) + ResponseEntity streamSingleResource(@NotBlank String bucket, @NotBlank String object, + @Nullable String range); /** * Download single resource response entity. @@ -51,6 +62,8 @@ ResponseEntity asyncStreamSingleResource(@NotBlank String * @param bucket the bucket * @param object the object * @return the response entity + * @deprecated since it's not async method, will be deleted in the future. */ + @Deprecated(forRemoval = true) ResponseEntity downloadSingleResource(@NotBlank String bucket, @NotBlank String object); } diff --git a/oss-center/src/main/java/com/jmsoftware/maf/osscenter/read/service/impl/ReadResourceServiceImpl.java b/oss-center/src/main/java/com/jmsoftware/maf/osscenter/read/service/impl/ReadResourceServiceImpl.java index 4eead721..873a2bdd 100644 --- a/oss-center/src/main/java/com/jmsoftware/maf/osscenter/read/service/impl/ReadResourceServiceImpl.java +++ b/oss-center/src/main/java/com/jmsoftware/maf/osscenter/read/service/impl/ReadResourceServiceImpl.java @@ -32,8 +32,9 @@ public class ReadResourceServiceImpl implements ReadResourceService { private final MinioHelper minioHelper; @Override - public ResponseEntity streamSingleResource(@NotBlank String bucket, @NotBlank String object, - @Nullable String range) { + public ResponseEntity asyncStreamSingleResource(@NotBlank String bucket, + @NotBlank String object, + @Nullable String range) { StatObjectResponse statObjectResponse; try { statObjectResponse = this.minioHelper.statObject(bucket, object); @@ -48,15 +49,41 @@ public ResponseEntity streamSingleResource(@NotBlank String bucket, @N .header(HttpHeaders.ACCEPT_RANGES, "bytes") .contentLength(statObjectResponse.size()) .contentType(MediaType.parseMediaType(statObjectResponse.contentType())) - .body(new InputStreamResource(getObjectResponse)); + .body(outputStream -> { + IoUtil.copy(getObjectResponse, outputStream); + IoUtil.close(getObjectResponse); + }); } - return this.getResourceRegion(bucket, object, statObjectResponse, httpRanges); + return this.asyncGetResourceRegion(bucket, object, statObjectResponse, httpRanges); } @Override - public ResponseEntity asyncStreamSingleResource(@NotBlank String bucket, - @NotBlank String object, - @Nullable String range) { + @SuppressWarnings("DuplicatedCode") + public ResponseEntity asyncDownloadSingleResource(@NotBlank String bucket, + @NotBlank String object) { + StatObjectResponse statObjectResponse; + try { + statObjectResponse = this.minioHelper.statObject(bucket, object); + } catch (Exception e) { + log.error("Exception occurred when looking for object. Exception message: {}", e.getMessage()); + return ResponseEntity.notFound().build(); + } + val getObjectResponse = this.minioHelper.getObject(bucket, object); + return ResponseEntity.ok() + .header(HttpHeaders.ACCEPT_RANGES, "bytes") + .header(HttpHeaders.CONTENT_DISPOSITION, + ContentDisposition.builder("attachment").filename(object).build().toString()) + .contentLength(statObjectResponse.size()) + .contentType(MediaType.parseMediaType(statObjectResponse.contentType())) + .body((outputStream -> { + IoUtil.copy(getObjectResponse, outputStream); + IoUtil.close(getObjectResponse); + })); + } + + @Override + public ResponseEntity streamSingleResource(@NotBlank String bucket, @NotBlank String object, + @Nullable String range) { StatObjectResponse statObjectResponse; try { statObjectResponse = this.minioHelper.statObject(bucket, object); @@ -71,15 +98,13 @@ public ResponseEntity asyncStreamSingleResource(@NotBlank .header(HttpHeaders.ACCEPT_RANGES, "bytes") .contentLength(statObjectResponse.size()) .contentType(MediaType.parseMediaType(statObjectResponse.contentType())) - .body(outputStream -> { - IoUtil.copy(getObjectResponse, outputStream); - IoUtil.close(getObjectResponse); - }); + .body(new InputStreamResource(getObjectResponse)); } - return this.asyncGetResourceRegion(bucket, object, statObjectResponse, httpRanges); + return this.getResourceRegion(bucket, object, statObjectResponse, httpRanges); } @Override + @SuppressWarnings("DuplicatedCode") public ResponseEntity downloadSingleResource(String bucket, String object) { StatObjectResponse statObjectResponse; try { @@ -99,9 +124,9 @@ public ResponseEntity downloadSingleResource(String bucket, String obj } @SuppressWarnings("DuplicatedCode") - private ResponseEntity getResourceRegion(String bucket, String object, - StatObjectResponse statObjectResponse, - List httpRanges) { + private ResponseEntity asyncGetResourceRegion(String bucket, String object, + StatObjectResponse statObjectResponse, + List httpRanges) { val getObjectResponse = this.minioHelper.getObject(bucket, object, httpRanges.get(0).getRangeStart(0), MEDIUM_CHUNK_SIZE.toBytes()); val start = httpRanges.get(0).getRangeStart(0); @@ -114,13 +139,27 @@ private ResponseEntity getResourceRegion(String bucket, String object, .header(HttpHeaders.CONTENT_RANGE, String.format("bytes %d-%d/%d", start, end, resourceLength)) .contentLength(rangeLength) .contentType(MediaType.parseMediaType(statObjectResponse.contentType())) - .body(new InputStreamResource(getObjectResponse)); + .body(outputStream -> { + IoUtil.copy(getObjectResponse, outputStream); + IoUtil.close(getObjectResponse); + }); } + /** + * Gets resource region. + * + * @param bucket the bucket + * @param object the object + * @param statObjectResponse the stat object response + * @param httpRanges the http ranges + * @return the resource region + * @deprecated since it's not async method, will be deleted in the future. + */ + @Deprecated(forRemoval = true) @SuppressWarnings("DuplicatedCode") - private ResponseEntity asyncGetResourceRegion(String bucket, String object, - StatObjectResponse statObjectResponse, - List httpRanges) { + private ResponseEntity getResourceRegion(String bucket, String object, + StatObjectResponse statObjectResponse, + List httpRanges) { val getObjectResponse = this.minioHelper.getObject(bucket, object, httpRanges.get(0).getRangeStart(0), MEDIUM_CHUNK_SIZE.toBytes()); val start = httpRanges.get(0).getRangeStart(0); @@ -133,9 +172,6 @@ private ResponseEntity asyncGetResourceRegion(String buck .header(HttpHeaders.CONTENT_RANGE, String.format("bytes %d-%d/%d", start, end, resourceLength)) .contentLength(rangeLength) .contentType(MediaType.parseMediaType(statObjectResponse.contentType())) - .body(outputStream -> { - IoUtil.copy(getObjectResponse, outputStream); - IoUtil.close(getObjectResponse); - }); + .body(new InputStreamResource(getObjectResponse)); } }