Skip to content

Commit

Permalink
feat($static-resource-center): support partial response
Browse files Browse the repository at this point in the history
  • Loading branch information
johnnymillergh committed Jun 24, 2021
1 parent 9cdaf54 commit 7ddf6a9
Show file tree
Hide file tree
Showing 3 changed files with 70 additions and 4 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,13 @@
import io.swagger.annotations.ApiOperation;
import lombok.RequiredArgsConstructor;
import org.springframework.core.io.Resource;
import org.springframework.core.io.support.ResourceRegion;
import org.springframework.http.HttpHeaders;
import org.springframework.http.ResponseEntity;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestHeader;
import org.springframework.web.bind.annotation.RestController;

import javax.validation.Valid;
Expand All @@ -34,4 +37,11 @@ public ResponseEntity<Resource> getSingleResource(@PathVariable String bucket, @
@Valid GetSingleResourcePayload payload) {
return readResourceService.getSingleResource(bucket, object, payload);
}

@GetMapping(value = "/partial/{bucket}/{object}", produces = "application/octet-stream")
@ApiOperation(value = "Get a partial resource", notes = "Get a partial resource")
public ResponseEntity<ResourceRegion> getPartialResource(@RequestHeader(value = HttpHeaders.RANGE) String rangeHeader,
@PathVariable String bucket, @PathVariable String object) {
return readResourceService.getResourceRegion(rangeHeader, bucket, object);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,14 @@

import com.jmsoftware.maf.authcenter.read.entity.GetSingleResourcePayload;
import org.springframework.core.io.Resource;
import org.springframework.core.io.support.ResourceRegion;
import org.springframework.http.ResponseEntity;
import org.springframework.validation.annotation.Validated;

import javax.validation.Valid;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotNull;
import java.io.IOException;

/**
* <h1>ReadResourceService</h1>
Expand All @@ -19,6 +21,7 @@
@Validated
public interface ReadResourceService {
String BUCKET_OBJECT_NAME_REGEX = "^.+/.+$";
long CHUNK_SIZE = 1024 * 1024 * 1024 * 4L;

/**
* Gets single resource.
Expand All @@ -30,4 +33,14 @@ public interface ReadResourceService {
*/
ResponseEntity<Resource> getSingleResource(@NotBlank String bucket, @NotBlank String object,
@Valid @NotNull GetSingleResourcePayload payload);

/**
* Gets resource region.
*
* @param header the header
* @param bucket the bucket
* @param object the object
* @return the resource region
*/
ResponseEntity<ResourceRegion> getResourceRegion(String header, String bucket, String object);
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,23 +2,26 @@

import cn.hutool.core.util.BooleanUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import com.jmsoftware.maf.authcenter.read.entity.GetSingleResourcePayload;
import com.jmsoftware.maf.authcenter.read.service.ReadResourceService;
import com.jmsoftware.maf.springcloudstarter.helper.MinioHelper;
import lombok.RequiredArgsConstructor;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import lombok.val;
import org.springframework.core.io.InputStreamResource;
import org.springframework.core.io.Resource;
import org.springframework.http.ContentDisposition;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.core.io.support.ResourceRegion;
import org.springframework.http.*;
import org.springframework.stereotype.Service;

import javax.validation.Valid;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotNull;
import java.io.IOException;

import static java.lang.Math.min;

/**
* <h1>ReadResourceServiceImpl</h1>
Expand Down Expand Up @@ -52,4 +55,44 @@ public ResponseEntity<Resource> getSingleResource(@NotBlank String bucket, @NotB
.contentType(MediaType.parseMediaType(statObjectResponse.contentType()))
.body(new InputStreamResource(inputStream));
}

@Override
public ResponseEntity<ResourceRegion> getResourceRegion(String header, String bucket, String object) {
val statObjectResponse = minioHelper.statObject("bucket", "object");
if (ObjectUtil.isNull(statObjectResponse)) {
return ResponseEntity.notFound().build();
}
val inputStream = minioHelper.getObject("bucket", "object");
final InputStreamResource inputStreamResource = new InputStreamResource(inputStream);
ResourceRegion resourceRegion = getResourceRegion(inputStreamResource, object);
return ResponseEntity.status(HttpStatus.PARTIAL_CONTENT)
.contentType(MediaTypeFactory.getMediaType(inputStreamResource)
.orElse(MediaType.APPLICATION_OCTET_STREAM))
.body(resourceRegion);
}

@SneakyThrows({IOException.class})
private ResourceRegion getResourceRegion(Resource resource, String httpHeaders) {
ResourceRegion resourceRegion;
long contentLength = resource.contentLength();
int fromRange = 0;
int toRange = 0;
if (StrUtil.isNotBlank(httpHeaders)) {
String[] ranges = httpHeaders.substring("bytes=".length()).split("-");
fromRange = Integer.parseInt(ranges[0]);
if (ranges.length > 1) {
toRange = Integer.parseInt(ranges[1]);
} else {
toRange = (int) (contentLength - 1);
}
}
if (fromRange > 0) {
long rangeLength = min(CHUNK_SIZE, toRange - fromRange + 1);
resourceRegion = new ResourceRegion(resource, fromRange, rangeLength);
} else {
long rangeLength = min(CHUNK_SIZE, contentLength);
resourceRegion = new ResourceRegion(resource, 0, rangeLength);
}
return resourceRegion;
}
}

0 comments on commit 7ddf6a9

Please sign in to comment.