Implement mime-type detection in S3 service
This commit is contained in:
@ -5,3 +5,4 @@ DATABASE_USERNAME=user
|
|||||||
S3_ENDPOINT=http://s3:9000
|
S3_ENDPOINT=http://s3:9000
|
||||||
S3_ACCESS_KEY=minioadmin
|
S3_ACCESS_KEY=minioadmin
|
||||||
S3_SECRET_KEY=minioadmin
|
S3_SECRET_KEY=minioadmin
|
||||||
|
S3_BUCKET=composer-dev
|
||||||
|
|||||||
11
pom.xml
11
pom.xml
@ -32,6 +32,7 @@
|
|||||||
|
|
||||||
<modelmapper.version>3.2.4</modelmapper.version>
|
<modelmapper.version>3.2.4</modelmapper.version>
|
||||||
<spring-dotenv.version>4.0.0</spring-dotenv.version>
|
<spring-dotenv.version>4.0.0</spring-dotenv.version>
|
||||||
|
<apache-tika.version>3.2.3</apache-tika.version>
|
||||||
</properties>
|
</properties>
|
||||||
<dependencyManagement>
|
<dependencyManagement>
|
||||||
<dependencies>
|
<dependencies>
|
||||||
@ -87,6 +88,16 @@
|
|||||||
<artifactId>spring-dotenv</artifactId>
|
<artifactId>spring-dotenv</artifactId>
|
||||||
<version>${spring-dotenv.version}</version>
|
<version>${spring-dotenv.version}</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.apache.tika</groupId>
|
||||||
|
<artifactId>tika-core</artifactId>
|
||||||
|
<version>${apache-tika.version}</version>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.apache.tika</groupId>
|
||||||
|
<artifactId>tika-parsers-standard-package</artifactId>
|
||||||
|
<version>${apache-tika.version}</version>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.postgresql</groupId>
|
<groupId>org.postgresql</groupId>
|
||||||
|
|||||||
@ -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
|
@Bean
|
||||||
public S3Client s3Client() {
|
public S3Client s3Client() {
|
||||||
return S3Client.builder()
|
return S3Client.builder()
|
||||||
.endpointOverride(URI.create(endpoint))
|
|
||||||
.region(Region.US_WEST_1)
|
.region(Region.US_WEST_1)
|
||||||
|
.forcePathStyle(true)
|
||||||
.credentialsProvider(() -> AwsBasicCredentials.create(accessKey, secretKey))
|
.credentialsProvider(() -> AwsBasicCredentials.create(accessKey, secretKey))
|
||||||
|
.endpointOverride(URI.create(endpoint))
|
||||||
.build();
|
.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