Implement mime-type detection in S3 service
This commit is contained in:
@ -0,0 +1,13 @@
|
||||
package com.bivashy.backend.composer.config;
|
||||
|
||||
import org.apache.tika.Tika;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
@Configuration
|
||||
public class MimeDetectionConfig {
|
||||
@Bean
|
||||
public Tika tika() {
|
||||
return new Tika();
|
||||
}
|
||||
}
|
||||
@ -22,9 +22,10 @@ public class StorageS3Config {
|
||||
@Bean
|
||||
public S3Client s3Client() {
|
||||
return S3Client.builder()
|
||||
.endpointOverride(URI.create(endpoint))
|
||||
.region(Region.US_WEST_1)
|
||||
.forcePathStyle(true)
|
||||
.credentialsProvider(() -> AwsBasicCredentials.create(accessKey, secretKey))
|
||||
.endpointOverride(URI.create(endpoint))
|
||||
.build();
|
||||
}
|
||||
|
||||
|
||||
@ -0,0 +1,37 @@
|
||||
package com.bivashy.backend.composer.controller;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.RequestParam;
|
||||
import org.springframework.web.bind.annotation.RequestPart;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
import com.bivashy.backend.composer.service.AudioBlobStorageService.Blob;
|
||||
import com.bivashy.backend.composer.service.AudioS3StorageService;
|
||||
|
||||
@RestController
|
||||
public class S3Controller {
|
||||
private final AudioS3StorageService s3Service;
|
||||
|
||||
public S3Controller(AudioS3StorageService s3Service) {
|
||||
this.s3Service = s3Service;
|
||||
}
|
||||
|
||||
@PostMapping(path = "/upload", consumes = { MediaType.MULTIPART_FORM_DATA_VALUE })
|
||||
public String upload(@RequestPart MultipartFile document) throws IOException {
|
||||
return s3Service.store(document.getBytes());
|
||||
}
|
||||
|
||||
@GetMapping(path = "/read")
|
||||
public ResponseEntity<byte[]> read(@RequestParam("document") String document) throws IOException {
|
||||
Blob blob = s3Service.read(document);
|
||||
return ResponseEntity.ok()
|
||||
.contentType(blob.contentType())
|
||||
.body(blob.stream().readAllBytes());
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,8 @@
|
||||
package com.bivashy.backend.composer.model;
|
||||
|
||||
public class SourceProviders {
|
||||
public static final String YOUTUBE = "YOUTUBE";
|
||||
public static final String LOCAL = "SOUNDCLOUD";
|
||||
public static final String EXTERNAL = "EXTERNAL";
|
||||
|
||||
}
|
||||
@ -0,0 +1,8 @@
|
||||
package com.bivashy.backend.composer.model;
|
||||
|
||||
public class SourceTypes {
|
||||
public static final String AUDIO = "VIDEO";
|
||||
public static final String PLAYLIST = "PLAYLIST";
|
||||
public static final String FILE = "FILE";
|
||||
public static final String URL = "URL";
|
||||
}
|
||||
@ -0,0 +1,19 @@
|
||||
package com.bivashy.backend.composer.service;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
|
||||
import org.springframework.http.MediaType;
|
||||
|
||||
public interface AudioBlobStorageService {
|
||||
String store(InputStream inputStream);
|
||||
|
||||
String store(byte[] data);
|
||||
|
||||
byte[] readRaw(String path) throws IOException;
|
||||
|
||||
Blob read(String path);
|
||||
|
||||
public record Blob(InputStream stream, MediaType contentType) {
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,87 @@
|
||||
package com.bivashy.backend.composer.service;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.util.UUID;
|
||||
|
||||
import org.apache.tika.Tika;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.http.InvalidMediaTypeException;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import software.amazon.awssdk.core.ResponseInputStream;
|
||||
import software.amazon.awssdk.core.sync.RequestBody;
|
||||
import software.amazon.awssdk.services.s3.S3Client;
|
||||
import software.amazon.awssdk.services.s3.model.GetObjectRequest;
|
||||
import software.amazon.awssdk.services.s3.model.GetObjectResponse;
|
||||
import software.amazon.awssdk.services.s3.model.PutObjectRequest;
|
||||
|
||||
@Service
|
||||
public class AudioS3StorageService implements AudioBlobStorageService {
|
||||
private static final Logger logger = LoggerFactory.getLogger(AudioS3StorageService.class);
|
||||
|
||||
private static final byte[] DEFAULT_BUFFER = new byte[8192];
|
||||
private final S3Client s3Client;
|
||||
private final Tika tika;
|
||||
@Value("${spring.s3.bucket}")
|
||||
private String bucket;
|
||||
|
||||
public AudioS3StorageService(S3Client s3Client, Tika tika) {
|
||||
this.s3Client = s3Client;
|
||||
this.tika = tika;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String store(InputStream inputStream) {
|
||||
return store(new ByteArrayInputStream(DEFAULT_BUFFER).readAllBytes());
|
||||
}
|
||||
|
||||
@Override
|
||||
public String store(byte[] data) {
|
||||
String objectKey = newObjectName();
|
||||
String contentType = detectContentType(data);
|
||||
s3Client.putObject(PutObjectRequest.builder()
|
||||
.bucket(bucket)
|
||||
.key(objectKey)
|
||||
.contentType(contentType)
|
||||
.build(), RequestBody.fromBytes(data));
|
||||
return String.join("/", bucket, objectKey);
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte[] readRaw(String path) throws IOException {
|
||||
return read(path).stream().readAllBytes();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Blob read(String path) {
|
||||
if (path.startsWith(bucket + "/")) {
|
||||
path = path.substring(bucket.length());
|
||||
}
|
||||
ResponseInputStream<GetObjectResponse> response = s3Client.getObject(GetObjectRequest.builder()
|
||||
.bucket(bucket)
|
||||
.key(path)
|
||||
.build());
|
||||
|
||||
MediaType mediaType = MediaType.APPLICATION_OCTET_STREAM;
|
||||
try {
|
||||
mediaType = MediaType.parseMediaType(response.response().contentType());
|
||||
} catch (InvalidMediaTypeException e) {
|
||||
logger.error("invalid media type", e);
|
||||
}
|
||||
return new Blob(response, mediaType);
|
||||
}
|
||||
|
||||
private String detectContentType(byte[] data) {
|
||||
return tika.detect(data);
|
||||
}
|
||||
|
||||
private String newObjectName() {
|
||||
return String.join("/", "audio", UUID.randomUUID().toString());
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user