Skip to content

Commit

Permalink
Merge pull request #30 from AWS-CV-Project-3355/feat/#29
Browse files Browse the repository at this point in the history
[Feat] Save Photos
  • Loading branch information
egg-z1 authored Nov 29, 2024
2 parents 0ccb13b + de91d0a commit b387192
Show file tree
Hide file tree
Showing 8 changed files with 367 additions and 8 deletions.
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -37,3 +37,6 @@ out/

### VS Code ###
.vscode/

### Mac OS ###
.DS_Store
7 changes: 7 additions & 0 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ configurations {

repositories {
mavenCentral()
maven { url 'https://oss.sonatype.org/content/repositories/releases' }
}

dependencies {
Expand All @@ -45,6 +46,12 @@ dependencies {

// Redis
// implementation 'org.springframework.boot:spring-boot-starter-data-redis'

// 이미지 추출 라이브러리
implementation 'org.bytedeco:javacv:1.5.8'
// implementation 'org.bytedeco:ffmpeg:7.1-1.5.8'
implementation 'net.bramp.ffmpeg:ffmpeg:0.6.2'

}

tasks.named('test') {
Expand Down
64 changes: 63 additions & 1 deletion src/main/java/aws/teamthreefive/aws/s3/AmazonS3Manager.java
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,23 @@
import aws.teamthreefive.uuid.entity.Uuid;
import aws.teamthreefive.uuid.repository.UuidRepository;
import com.amazonaws.services.s3.AmazonS3;
import com.amazonaws.services.s3.model.GetObjectRequest;
import com.amazonaws.services.s3.model.ObjectMetadata;
import com.amazonaws.services.s3.model.PutObjectRequest;
import com.amazonaws.services.s3.model.S3Object;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import org.springframework.web.multipart.MultipartFile;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.UUID;

@Slf4j
@Component
Expand Down Expand Up @@ -45,4 +54,57 @@ public String generatePhotoKeyName(Uuid uuid){
return amazonConfig.getPhotoPath() + '/' + uuid.getUuid();
}

}
// 새로 추가된 메서드들
public File downloadFileToTemp(String fileUrl) throws IOException {
try {
// URL에서 버킷과 키 추출
URL url = new URL(fileUrl);
String bucket = amazonConfig.getBucket();
String key = extractKeyFromUrl(url);

// 임시 파일 생성
Path tempDir = Files.createTempDirectory("s3-downloads-");
File tempFile = Files.createTempFile(tempDir, "download-", ".mp4").toFile();
tempFile.deleteOnExit();

// S3에서 객체 다운로드
S3Object s3Object = amazonS3.getObject(new GetObjectRequest(bucket, key));
try (InputStream inputStream = s3Object.getObjectContent();
FileOutputStream outputStream = new FileOutputStream(tempFile)) {

byte[] buffer = new byte[1024];
int bytesRead;
while ((bytesRead = inputStream.read(buffer)) != -1) {
outputStream.write(buffer, 0, bytesRead);
}
}

log.info("Downloaded file from S3: {} to temporary location: {}", fileUrl, tempFile.getAbsolutePath());
return tempFile;
} catch (Exception e) {
log.error("Error downloading file from S3: {}", fileUrl, e);
throw new IOException("S3 파일 다운로드 중 오류 발생", e);
}
}

// S3 URL에서 키 추출하는 헬퍼 메서드
private String extractKeyFromUrl(URL url) {
String path = url.getPath();
// URL path가 "/버킷이름/video/키" 형식일 경우
String[] parts = path.split("/", 4); // path를 "/" 기준으로 분리
return parts.length >= 4 ? parts[3] : path.substring(1); // 4번째 파트를 반환 (키)
}


// MultipartFile을 File로 변환하는 유틸리티 메서드 (필요한 경우)
public File convertMultiPartToFile(MultipartFile file) throws IOException {
File convertedFile = File.createTempFile(
UUID.randomUUID().toString(),
".tmp"
);
try (FileOutputStream fos = new FileOutputStream(convertedFile)) {
fos.write(file.getBytes());
}
return convertedFile;
}
}
Original file line number Diff line number Diff line change
@@ -1,15 +1,24 @@
package aws.teamthreefive.diecastvideo.controller;

import aws.teamthreefive.aws.s3.AmazonS3Manager;
import aws.teamthreefive.diecastvideo.converter.DiecastvideoConverter;
import aws.teamthreefive.diecastvideo.dto.DiecastvideoRequestDTO;
import aws.teamthreefive.diecastvideo.dto.DiecastvideoResponseDTO;
import aws.teamthreefive.diecastvideo.entity.Diecastvideo;
import aws.teamthreefive.diecastvideo.repository.FrameRepository;
import aws.teamthreefive.diecastvideo.service.DiecastvideoCommandService;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.responses.ApiResponse;
import lombok.RequiredArgsConstructor;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.bind.annotation.*;

import java.io.File;
import java.util.List;


// DiecastvideoController.java
@CrossOrigin
@RestController
@RequiredArgsConstructor
Expand All @@ -18,17 +27,40 @@ public class DiecastvideoController {

private final DiecastvideoCommandService diecastvideoCommandService;

private final AmazonS3Manager s3Manager;
private static final Logger log = LoggerFactory.getLogger(DiecastvideoCommandService.class);

// 영상 저장
@PostMapping(consumes = "multipart/form-data")
@Operation(summary = "영상 저장 API", description = "업로드된 영상 저장")
public DiecastvideoResponseDTO.SaveDiecastvideoResultDTO saveDiecastvideo(
@ModelAttribute DiecastvideoRequestDTO.DiecastvideoDTO request
) {

Diecastvideo diecastvideo = diecastvideoCommandService.saveDiecastvideo(request);

return DiecastvideoConverter.toSaveDiecastvideoResultDTO(diecastvideo);

}

}
@GetMapping("/frames")
@Operation(summary = "MP4 영상의 프레임 추출 API", description = "MP4 영상에서 초 단위로 이미지를 추출")
public DiecastvideoResponseDTO.FrameResultDTO extractFramesFromVideo(
@RequestParam("videoId") Long videoId
) {
// 영상 정보 조회
String videoUrl = diecastvideoCommandService.getVideoUrlById(videoId);

// 실제 프레임 추출 로직 추가
try {
// 1. Download the video from S3 to a temporary file
File videoFile = s3Manager.downloadFileToTemp(videoUrl);

// 2. Extract frames and upload them
List<String> frameUrls = diecastvideoCommandService.extractAndUploadFrames(videoFile, videoId);

// 3. Return the extracted frames
return diecastvideoCommandService.extractFrames(videoId);
} catch (Exception e) {
log.error("Frame extraction failed for video ID {}: {}", videoId, e.getMessage(), e);
throw new RuntimeException("프레임 추출에 실패했습니다.", e);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
import lombok.NoArgsConstructor;

import java.time.LocalDateTime;
import java.util.List;

public class DiecastvideoResponseDTO {

Expand All @@ -19,4 +20,25 @@ public static class SaveDiecastvideoResultDTO {
LocalDateTime createdAt;
}


@Builder
@Getter
@NoArgsConstructor
@AllArgsConstructor
public static class FrameDTO {
private String photoUuid;
private String photoUrl;
private int photoPosition;
}

@Builder
@Getter
@NoArgsConstructor
@AllArgsConstructor
public static class FrameResultDTO {
private Long objectId;
private List<FrameDTO> frame;
}


}
24 changes: 24 additions & 0 deletions src/main/java/aws/teamthreefive/diecastvideo/entity/Frame.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package aws.teamthreefive.diecastvideo.entity;

import jakarta.persistence.*;
import lombok.Getter;
import lombok.Setter;

@Entity
@Getter
@Setter

@Table(name = "frame")
public class Frame {

@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private Long videoId; // Diecastvideo와 연관된 ID
private String photoUuid;
private String photoUrl;
private int photoPosition;

// Getters, Setters, Constructors

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package aws.teamthreefive.diecastvideo.repository;

import aws.teamthreefive.diecastvideo.entity.Frame;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;

import java.util.List;

@Repository
public interface FrameRepository extends JpaRepository<Frame, Long> {
List<Frame> findByVideoId(Long videoId);
}
Loading

0 comments on commit b387192

Please sign in to comment.