Skip to content

Commit

Permalink
feat($OSS): support download resource
Browse files Browse the repository at this point in the history
  • Loading branch information
johnnymillergh committed Aug 10, 2021
1 parent beaa80d commit 131d49f
Show file tree
Hide file tree
Showing 5 changed files with 67 additions and 58 deletions.
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
package com.jmsoftware.maf.osscenter.read.controller;

import com.jmsoftware.maf.osscenter.read.entity.GetSingleResourcePayload;
import com.jmsoftware.maf.osscenter.read.service.ReadResourceService;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
Expand All @@ -14,8 +13,6 @@
import org.springframework.web.bind.annotation.RequestHeader;
import org.springframework.web.bind.annotation.RestController;

import javax.validation.Valid;

/**
* <h1>ReadResourceController</h1>
* <p>
Expand All @@ -30,11 +27,16 @@
public class ReadResourceController {
private final ReadResourceService readResourceService;

@GetMapping("/{bucket}/{object}")
@ApiOperation(value = "Get single resource", notes = "Get or download single resource")
public ResponseEntity<Resource> getSingleResource(@PathVariable String bucket, @PathVariable String object,
@Valid GetSingleResourcePayload payload,
@RequestHeader(name = HttpHeaders.RANGE, required = false) String range) {
return this.readResourceService.getSingleResource(bucket, object, payload, range);
@GetMapping("/stream/{bucket}/{object}")
@ApiOperation(value = "Stream single resource", notes = "Stream single resource")
public ResponseEntity<Resource> streamSingleResource(@PathVariable String bucket, @PathVariable String object,
@RequestHeader(name = HttpHeaders.RANGE, required = false) String range) {
return this.readResourceService.streamSingleResource(bucket, object, range);
}

@GetMapping("/download/{bucket}/{object}")
@ApiOperation(value = "Download single resource", notes = "Download single resource")
public ResponseEntity<Resource> downloadSingleResource(@PathVariable String bucket, @PathVariable String object) {
return this.readResourceService.downloadSingleResource(bucket, object);
}
}

This file was deleted.

Original file line number Diff line number Diff line change
@@ -1,14 +1,12 @@
package com.jmsoftware.maf.osscenter.read.service;

import com.jmsoftware.maf.osscenter.read.entity.GetSingleResourcePayload;
import org.springframework.core.io.Resource;
import org.springframework.http.ResponseEntity;
import org.springframework.lang.Nullable;
import org.springframework.util.unit.DataSize;
import org.springframework.validation.annotation.Validated;

import javax.validation.Valid;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotNull;

/**
* <h1>ReadResourceService</h1>
Expand All @@ -20,18 +18,27 @@
@Validated
public interface ReadResourceService {
String BUCKET_OBJECT_NAME_REGEX = "^.+/.+$";
long CHUNK_SIZE = 1024 * 1024 * 1024 * 4L;
DataSize SMALL_CHUNK_SIZE = DataSize.ofMegabytes(1);
DataSize MEDIUM_CHUNK_SIZE = DataSize.ofMegabytes(4);
DataSize LARGE_CHUNK_SIZE = DataSize.ofMegabytes(8);

/**
* Gets single resource.
* Stream single resource.
*
* @param bucket the bucket
* @param object the object
* @param payload the payload
* @param range
* @param bucket the bucket
* @param object the object
* @param range the range
* @return the single resource
*/
ResponseEntity<Resource> getSingleResource(@NotBlank String bucket, @NotBlank String object,
@Valid @NotNull GetSingleResourcePayload payload,
@Nullable String range);
ResponseEntity<Resource> streamSingleResource(@NotBlank String bucket, @NotBlank String object,
@Nullable String range);

/**
* Download single resource response entity.
*
* @param bucket the bucket
* @param object the object
* @return the response entity
*/
ResponseEntity<Resource> downloadSingleResource(@NotBlank String bucket, @NotBlank String object);
}
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
package com.jmsoftware.maf.osscenter.read.service.impl;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.BooleanUtil;
import com.jmsoftware.maf.osscenter.read.entity.GetSingleResourcePayload;
import com.jmsoftware.maf.osscenter.read.service.ReadResourceService;
import com.jmsoftware.maf.springcloudstarter.helper.MinioHelper;
import io.minio.StatObjectResponse;
Expand All @@ -14,11 +12,8 @@
import org.springframework.http.*;
import org.springframework.lang.Nullable;
import org.springframework.stereotype.Service;
import org.springframework.util.unit.DataSize;

import javax.validation.Valid;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotNull;
import java.util.List;

/**
Expand All @@ -32,15 +27,11 @@
@Service
@RequiredArgsConstructor
public class ReadResourceServiceImpl implements ReadResourceService {
private static final DataSize SMALL_DATA_SIZE = DataSize.ofMegabytes(1);
private static final DataSize MEDIUM_DATA_SIZE = DataSize.ofMegabytes(4);
private static final DataSize LARGE_DATA_SIZE = DataSize.ofMegabytes(8);
private final MinioHelper minioHelper;

@Override
public ResponseEntity<Resource> getSingleResource(@NotBlank String bucket, @NotBlank String object,
@Valid @NotNull GetSingleResourcePayload payload,
@Nullable String range) {
public ResponseEntity<Resource> streamSingleResource(@NotBlank String bucket, @NotBlank String object,
@Nullable String range) {
StatObjectResponse statObjectResponse;
try {
statObjectResponse = this.minioHelper.statObject(bucket, object);
Expand All @@ -50,13 +41,8 @@ public ResponseEntity<Resource> getSingleResource(@NotBlank String bucket, @NotB
}
val httpRanges = HttpRange.parseRanges(range);
if (CollUtil.isEmpty(httpRanges)) {
val bodyBuilder = ResponseEntity.ok();
if (BooleanUtil.isTrue(payload.getDownloadable())) {
bodyBuilder.header(HttpHeaders.CONTENT_DISPOSITION,
ContentDisposition.builder("attachment").filename(object).build().toString());
}
val getObjectResponse = this.minioHelper.getObject(bucket, object, 0, MEDIUM_DATA_SIZE.toBytes());
return bodyBuilder
val getObjectResponse = this.minioHelper.getObject(bucket, object, 0, MEDIUM_CHUNK_SIZE.toBytes());
return ResponseEntity.ok()
.header(HttpHeaders.ACCEPT_RANGES, "bytes")
.contentLength(statObjectResponse.size())
.contentType(MediaType.parseMediaType(statObjectResponse.contentType()))
Expand All @@ -65,14 +51,34 @@ public ResponseEntity<Resource> getSingleResource(@NotBlank String bucket, @NotB
return this.getResourceRegion(bucket, object, statObjectResponse, httpRanges);
}

@Override
public ResponseEntity<Resource> downloadSingleResource(String bucket, 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 bodyBuilder = ResponseEntity.ok()
.header(HttpHeaders.CONTENT_DISPOSITION,
ContentDisposition.builder("attachment").filename(object).build().toString());
val getObjectResponse = this.minioHelper.getObject(bucket, object);
return bodyBuilder
.header(HttpHeaders.ACCEPT_RANGES, "bytes")
.contentLength(statObjectResponse.size())
.contentType(MediaType.parseMediaType(statObjectResponse.contentType()))
.body(new InputStreamResource(getObjectResponse));
}

private ResponseEntity<Resource> getResourceRegion(String bucket, String object,
StatObjectResponse statObjectResponse,
List<HttpRange> httpRanges) {
val bodyBuilder = ResponseEntity.status(HttpStatus.PARTIAL_CONTENT);
val getObjectResponse = this.minioHelper.getObject(bucket, object, httpRanges.get(0).getRangeStart(0),
MEDIUM_DATA_SIZE.toBytes());
MEDIUM_CHUNK_SIZE.toBytes());
val start = httpRanges.get(0).getRangeStart(0);
var end = start + MEDIUM_DATA_SIZE.toBytes() - 1;
var end = start + MEDIUM_CHUNK_SIZE.toBytes() - 1;
val resourceLength = statObjectResponse.size();
end = Math.min(end, resourceLength - 1);
val rangeLength = end - start + 1;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
import com.jmsoftware.maf.springcloudstarter.util.RequestUtil;
import lombok.extern.slf4j.Slf4j;
import lombok.val;
import org.apache.catalina.connector.ClientAbortException;
import org.springframework.context.support.DefaultMessageSourceResolvable;
import org.springframework.core.annotation.Order;
import org.springframework.http.HttpStatus;
Expand Down Expand Up @@ -163,21 +164,29 @@ public ResponseBodyBean<?> handleException(HttpServletRequest request, HttpServl
val exceptionCause = (BaseException) exception.getCause();
val code = exceptionCause.getCode();
response.setStatus(code);
return ResponseBodyBean.ofStatus(HttpStatus.valueOf(code), this.removeLineSeparator(exception.getMessage()));
return ResponseBodyBean.ofStatus(HttpStatus.valueOf(code),
this.removeLineSeparator(exception.getMessage()));
}
response.setStatus(HttpStatus.FORBIDDEN.value());
return ResponseBodyBean.ofStatus(HttpStatus.FORBIDDEN.value(), this.removeLineSeparator(exception.getMessage()),
null);
}

@ExceptionHandler(ClientAbortException.class)
public void handleException(HttpServletRequest request, ClientAbortException exception) {
this.requestLog(request);
log.error("An abort of a request by a remote client. Exception message: {} ", exception.getMessage());
}

@ExceptionHandler(UndeclaredThrowableException.class)
@ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
public ResponseBodyBean<?> handleError(UndeclaredThrowableException exception) {
log.error("Undeclared throwable exception occurred! Exception message: {} ", exception.getMessage(), exception);
if (ObjectUtil.isNotNull(exception.getCause()) && StrUtil.isNotEmpty(exception.getCause().getMessage())) {
return ResponseBodyBean.ofStatus(HttpStatus.INTERNAL_SERVER_ERROR,
String.format("Exception message: %s",
this.removeLineSeparator(exception.getCause().getMessage())));
this.removeLineSeparator(
exception.getCause().getMessage())));
}
return ResponseBodyBean.ofStatus(HttpStatus.INTERNAL_SERVER_ERROR,
String.format("Exception message: %s",
Expand Down

0 comments on commit 131d49f

Please sign in to comment.