Skip to content

Commit

Permalink
perf($OSS): enhance file merge process by detecting content type
Browse files Browse the repository at this point in the history
  • Loading branch information
johnnymillergh committed Aug 18, 2021
1 parent 753b816 commit 47ad2d4
Show file tree
Hide file tree
Showing 4 changed files with 59 additions and 22 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import com.jmsoftware.maf.common.bean.ResponseBodyBean;
import com.jmsoftware.maf.osscenter.write.entity.MergeResourceChunkPayload;
import com.jmsoftware.maf.osscenter.write.entity.ObjectResponse;
import com.jmsoftware.maf.osscenter.write.entity.UploadResourceChunkPayload;
import com.jmsoftware.maf.osscenter.write.service.WriteResourceService;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
Expand Down Expand Up @@ -38,13 +39,11 @@ public ResponseBodyBean<ObjectResponse> uploadSingleResource(@RequestParam("file
LocaleContextHolder.getLocale()));
}

@PostMapping("/upload/chunk/{chunkNumber}")
@PostMapping("/upload/chunk")
@ApiOperation(value = "Upload chunk of resource", notes = "Upload chunk of resource")
public ResponseBodyBean<ObjectResponse> uploadResourceChunk(@RequestParam("file") MultipartFile multipartFile,
@RequestParam(required = false) String bucket,
@PathVariable Integer chunkNumber) {
return ResponseBodyBean.ofSuccess(
this.writeResourceService.uploadResourceChunk(multipartFile, bucket, chunkNumber));
@Valid UploadResourceChunkPayload payload) {
return ResponseBodyBean.ofSuccess(this.writeResourceService.uploadResourceChunk(multipartFile, payload));
}

@PutMapping("/merge/chunk")
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
package com.jmsoftware.maf.osscenter.write.entity;

import com.jmsoftware.maf.osscenter.write.service.WriteResourceService;
import lombok.Data;
import org.hibernate.validator.constraints.Range;

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

/**
* Description: UploadResourceChunkPayload, change description here.
*
* @author Johnny Miller (锺俊), email: johnnysviva@outlook.com, date: 8/18/2021 9:29 AM
**/
@Data
public class UploadResourceChunkPayload {
private String bucket;
@NotNull
@Range(max = WriteResourceService.MAX_CHUNK_NUMBER)
private Integer chunkNumber;
@NotBlank
@Pattern(regexp = "^[^<>:;,?\"*|/]+$")
private String filename;
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,7 @@

import com.jmsoftware.maf.osscenter.write.entity.MergeResourceChunkPayload;
import com.jmsoftware.maf.osscenter.write.entity.ObjectResponse;
import org.hibernate.validator.constraints.Range;
import org.springframework.lang.Nullable;
import com.jmsoftware.maf.osscenter.write.entity.UploadResourceChunkPayload;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.multipart.MultipartFile;

Expand Down Expand Up @@ -33,13 +32,11 @@ public interface WriteResourceService {
* Upload resource chunk string.
*
* @param multipartFile the multipart file
* @param bucket the bucket
* @param chunkNumber the chunk number
* @param payload the payload
* @return the string
*/
ObjectResponse uploadResourceChunk(@NotNull MultipartFile multipartFile,
@Nullable String bucket,
@NotNull @Range(max = MAX_CHUNK_NUMBER) Integer chunkNumber);
@Valid @NotNull UploadResourceChunkPayload payload);

/**
* Merge resource chunk string.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,25 +3,27 @@
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.NumberUtil;
import cn.hutool.core.util.StrUtil;
import com.jmsoftware.maf.osscenter.read.service.ReadResourceService;
import com.jmsoftware.maf.osscenter.write.entity.MergeResourceChunkPayload;
import com.jmsoftware.maf.osscenter.write.entity.ObjectResponse;
import com.jmsoftware.maf.osscenter.write.entity.UploadResourceChunkPayload;
import com.jmsoftware.maf.osscenter.write.service.WriteResourceService;
import com.jmsoftware.maf.springcloudstarter.minio.MinioHelper;
import io.minio.ComposeSource;
import lombok.Cleanup;
import lombok.RequiredArgsConstructor;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import lombok.val;
import org.apache.tika.Tika;
import org.apache.tika.mime.MediaType;
import org.hibernate.validator.constraints.Range;
import org.springframework.lang.Nullable;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;

import javax.validation.Valid;
import javax.validation.constraints.NotNull;
import java.io.IOException;
import java.io.InputStream;
import java.util.HashMap;
import java.util.List;
import java.util.Objects;
Expand Down Expand Up @@ -85,19 +87,19 @@ public ObjectResponse uploadSingleResource(@NotNull MultipartFile multipartFile)
@Override
@SneakyThrows
public ObjectResponse uploadResourceChunk(@NotNull MultipartFile multipartFile,
@Nullable String bucket,
@NotNull @Range(max = MAX_CHUNK_NUMBER) Integer chunkNumber) {
@Valid @NotNull UploadResourceChunkPayload payload) {
if (StrUtil.isBlank(multipartFile.getOriginalFilename())) {
throw new IllegalArgumentException("File name required");
}
MediaType mediaType = null;
if (StrUtil.isBlank(bucket)) {
if (StrUtil.isBlank(payload.getBucket())) {
mediaType = this.parseMediaType(multipartFile);
}
// bucketName is either mediaType of given 'bucket'
val bucketName = StrUtil.isBlank(bucket) ? Objects.requireNonNull(mediaType).getType() : bucket;
val orderedFilename = String.format("%s.chunk%s", multipartFile.getOriginalFilename(),
NumberUtil.decimalFormat("000", chunkNumber));
val bucketName = StrUtil.isBlank(payload.getBucket()) ?
Objects.requireNonNull(mediaType).getType() : payload.getBucket();
val orderedFilename = String.format("%s.chunk%s", payload.getFilename(),
NumberUtil.decimalFormat("000", payload.getChunkNumber()));
val objectResponse = new ObjectResponse();
objectResponse.setBucket(bucketName);
objectResponse.setObject(orderedFilename);
Expand All @@ -109,16 +111,20 @@ public ObjectResponse uploadResourceChunk(@NotNull MultipartFile multipartFile,
}

@Override
@SneakyThrows
public ObjectResponse mergeResourceChunk(@Valid @NotNull MergeResourceChunkPayload payload) {
val objectName = this.validateObject(payload.getObjectList());
val sources = payload.getObjectList()
.stream()
.map(object -> ComposeSource.builder().bucket(payload.getBucket()).object(object).build())
.collect(Collectors.toList());
val statObjectResponse = this.minioHelper.statObject(payload.getBucket(),
CollUtil.getFirst(payload.getObjectList()));
@Cleanup val firstChunk = this.minioHelper.getObject(payload.getBucket(),
CollUtil.getFirst(payload.getObjectList()),
0,
ReadResourceService.TINY_CHUNK_SIZE.toBytes());
val mediaType = this.parseMediaType(firstChunk);
val headers = new HashMap<String, String>(4);
headers.put("Content-Type", statObjectResponse.contentType());
headers.put("Content-Type", mediaType.toString());
val objectWriteResponse = this.minioHelper.composeObject(payload.getBucket(), objectName, sources, headers);
val objectResponse = new ObjectResponse();
objectResponse.setBucket(objectWriteResponse.bucket());
Expand Down Expand Up @@ -151,4 +157,14 @@ private MediaType parseMediaType(MultipartFile multipartFile) throws IOException
}
return MediaType.parse(detectedMediaType);
}

private MediaType parseMediaType(InputStream inputStream) throws IOException {
val tika = new Tika();
val detectedMediaType = tika.detect(inputStream);
log.info("Detected media type: {}", detectedMediaType);
if (StrUtil.isBlank(detectedMediaType)) {
throw new IllegalStateException("Media extension detection failed!");
}
return MediaType.parse(detectedMediaType);
}
}

0 comments on commit 47ad2d4

Please sign in to comment.