Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -11,16 +11,19 @@ public record AssignmentAttachmentDto(
String value,
String originalFileName,
String contentType,
Long size
Long size,
String downloadUrl
) {
public static AssignmentAttachmentDto from(AssignmentAttachment assignmentAttachment){

public static AssignmentAttachmentDto from(AssignmentAttachment assignmentAttachment, String downloadUrl){
return new AssignmentAttachmentDto(
assignmentAttachment.getAssignmentAttachmentId(),
assignmentAttachment.getType(),
assignmentAttachment.getValue(),
assignmentAttachment.getOriginalFileName(),
assignmentAttachment.getContentType(),
assignmentAttachment.getSize()
assignmentAttachment.getSize(),
downloadUrl
);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -15,24 +15,17 @@ public record AssignmentDto(
@JsonFormat(pattern = "yyyy-MM-dd'T'HH:mm", timezone = "Asia/Seoul") LocalDateTime startDate,
@JsonFormat(pattern = "yyyy-MM-dd'T'HH:mm", timezone = "Asia/Seoul") LocalDateTime endDate,
String userName,

// 과제 첨부 데이터
List<AssignmentAttachmentDto> attachmentDtos
) {
public static AssignmentDto from(Assignment assignment, List<AssignmentAttachment> assignmentAttachments){

List<AssignmentAttachmentDto> assignmentResponseDtos = assignmentAttachments.stream()
.map(aa -> AssignmentAttachmentDto.from(aa))
.toList();

public static AssignmentDto from(Assignment assignment, List<AssignmentAttachmentDto> attachmentDtos){
return new AssignmentDto(
assignment.getAssignmentId(),
assignment.getTitle(),
assignment.getContent(),
assignment.getStartDate(),
assignment.getEndDate(),
assignment.getUser().getUsername(),
assignmentResponseDtos
attachmentDtos
);
}
}
Original file line number Diff line number Diff line change
@@ -1,12 +1,20 @@
package hello.cluebackend.application.directory.dto;

import jakarta.annotation.Nullable;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotNull;
import lombok.Getter;

import java.util.UUID;

@Getter
public class RequestDirectoryDto {
@Nullable
private UUID directoryId;

@NotNull(message="클래스룸 ID는 필수입니다.")
private UUID classRoomId;

@NotBlank(message = "디렉토리 이름은 필수입니다.")
private String name;
}
Original file line number Diff line number Diff line change
Expand Up @@ -13,16 +13,19 @@ public record SubmissionAttachmentResponse(
String value,
String originalFileName,
String contentType,
Long size
Long size,
String downloadUrl
) {
public static SubmissionAttachmentResponse from(SubmissionAttachment submissionAttachment){

public static SubmissionAttachmentResponse from(SubmissionAttachment submissionAttachment, String downloadUrl){
return new SubmissionAttachmentResponse(
submissionAttachment.getSubmissionAttachmentId(),
submissionAttachment.getType(),
submissionAttachment.getValue(),
submissionAttachment.getOriginalFileName(),
submissionAttachment.getContentType(),
submissionAttachment.getSize()
submissionAttachment.getSize(),
downloadUrl
);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -43,12 +43,23 @@ public AssignmentAttachment findAssignmentAttachmentByIdOrderThrow(UUID userId,
.orElseThrow(() -> new EntityNotFoundException("해당 첨부 파일을 찾을수 없습니다."));
}

// 과제 단일 조회
public AssignmentDto findById(UUID assignmentId) {
Assignment a = findByIdOrThrow(assignmentId);
List<AssignmentAttachment> assignmentAttachments = assignmentAttachmentJpaRepository.findAllByAssignment(a);

return AssignmentDto.from(a, assignmentAttachments);
List<AssignmentAttachmentDto> attachmentDtos = assignmentAttachments.stream()
.map(attachment -> {
String downloadUrl = null;
if (attachment.getType() == hello.cluebackend.domain.assignment.model.FileType.FILE) {
downloadUrl = fileService.getPresignedDownloadUrl(attachment.getValue());
} else if (attachment.getType() == hello.cluebackend.domain.assignment.model.FileType.URL) {
downloadUrl = attachment.getValue();
}
return AssignmentAttachmentDto.from(attachment, downloadUrl);
})
.toList();

return AssignmentDto.from(a, attachmentDtos);
}

// 사용자가 속한 모든 수업 과제 조회
Expand Down Expand Up @@ -80,17 +91,30 @@ public Assignment findByIdOrThrow(UUID assignmentId) {
.orElseThrow(() -> new EntityNotFoundException("해당 과제를 찾을수 없습니다."));
}

public Resource downloadAttachment(AssignmentAttachment assignmentAttachment) throws IOException {
String path = assignmentAttachment.getValue();
return fileService.downloadFile(path);
public String getAttachmentDownloadUrl(UUID userId, UUID attachmentId) {
AssignmentAttachment attachment = findAssignmentAttachmentByIdOrderThrow(userId, attachmentId);

if (attachment.getType() == hello.cluebackend.domain.assignment.model.FileType.FILE) {
return fileService.getPresignedDownloadUrl(attachment.getValue());
}

return attachment.getValue();
}

// 과제 첨부 파일 목록 조회
// 과제 첨부 파일 목록 조회 (downloadUrl 포함)
public List<AssignmentAttachmentDto> findAttachmentsByAssignmentId(UUID assignmentId) {
Assignment assignment = findByIdOrThrow(assignmentId);
List<AssignmentAttachment> attachments = assignmentAttachmentJpaRepository.findAllByAssignment(assignment);
return attachments.stream()
.map(AssignmentAttachmentDto::from)
.map(attachment -> {
String downloadUrl = null;
if (attachment.getType() == hello.cluebackend.domain.assignment.model.FileType.FILE) {
downloadUrl = fileService.getPresignedDownloadUrl(attachment.getValue());
} else if (attachment.getType() == hello.cluebackend.domain.assignment.model.FileType.URL) {
downloadUrl = attachment.getValue();
}
return AssignmentAttachmentDto.from(attachment, downloadUrl);
})
.toList();
}
}
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
package hello.cluebackend.domain.file.service;

import com.amazonaws.HttpMethod;
import com.amazonaws.services.s3.AmazonS3Client;
import com.amazonaws.services.s3.model.ObjectMetadata;
import com.amazonaws.services.s3.model.PutObjectRequest;
import com.amazonaws.services.s3.model.S3Object;
import hello.cluebackend.domain.file.exception.S3FileNotFoundException;
import lombok.RequiredArgsConstructor;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.core.io.InputStreamResource;
Expand All @@ -13,6 +13,7 @@
import org.springframework.web.multipart.MultipartFile;

import java.io.IOException;
import java.util.Date;
import java.util.UUID;

@Service
Expand All @@ -23,7 +24,8 @@ public class FileService {
@Value("${cloud.aws.s3.bucket}")
private String bucket;

// 파일 추가
private static final int DEFAULT_EXPIRATION_MINUTES = 60;

public String storeFile(MultipartFile file) throws IOException {
String originalFilename = file.getOriginalFilename();
String extension = "";
Expand All @@ -36,18 +38,26 @@ public String storeFile(MultipartFile file) throws IOException {
metadata.setContentLength(file.getSize());
metadata.setContentType(file.getContentType());

amazonS3Client.putObject(new PutObjectRequest(bucket, storedFileName, file.getInputStream(), metadata)); // 공개 권한 설정
amazonS3Client.putObject(new PutObjectRequest(bucket, storedFileName, file.getInputStream(), metadata));
return storedFileName;
}

// 파일 삭제
public void deleteFile(String storedFileName) {
amazonS3Client.deleteObject(bucket, storedFileName);
}

// 파일 다운로드
@Deprecated
public Resource downloadFile(String filePath) {
S3Object s3Object = amazonS3Client.getObject(bucket, filePath);
return new InputStreamResource(s3Object.getObjectContent());
}

public String getPresignedDownloadUrl(String storedFileName) {
return getPresignedDownloadUrl(storedFileName, DEFAULT_EXPIRATION_MINUTES);
}

public String getPresignedDownloadUrl(String storedFileName, int expirationMinutes) {
Date expiration = new Date(System.currentTimeMillis() + expirationMinutes * 60 * 1000);
return amazonS3Client.generatePresignedUrl(bucket, storedFileName, expiration, HttpMethod.GET).toString();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ public class SubmissionCommandService {
private final UserService userService;
private final ClassRoomMapper classRoomMapper;

// 과제 전체 조회 및 과제 첨부 파일 조회
// 과제 전체 조회 및 과제 첨부 파일 조회 (downloadUrl 포함)
public List<SubmissionResponse> findAllByAssignmentId(UUID userId, UUID classId) {
UserEntity user = userService.findById(userId);
ClassRoom classRoom = classRoomMapper.fromClassRoomDtoToEntity(classRoomQueryService.findById(userId, classId));
Expand All @@ -48,15 +48,18 @@ public List<SubmissionResponse> findAllByAssignmentId(UUID userId, UUID classId)
.map(submission -> {
List<SubmissionAttachmentResponse> submissionAttachmentResponses =
submissionAttachmentJpaRepository.findAllBySubmission(submission).stream()
.map(SubmissionAttachmentResponse::from)
.map(attachment -> {
String downloadUrl = getDownloadUrlForAttachment(attachment);
return SubmissionAttachmentResponse.from(attachment, downloadUrl);
})
.toList();

return SubmissionResponse.from(submission, submissionAttachmentResponses);
})
.toList();
}

// 과제 제출 단일 조회
// 과제 제출 단일 조회 (downloadUrl 포함)
public SubmissionResponse findByAssignmentId(UUID userId, UUID submissionId) {
Submission submission = findByIdOrThrow(submissionId);
UserEntity requestUser = userService.findById(userId);
Expand All @@ -67,7 +70,10 @@ public SubmissionResponse findByAssignmentId(UUID userId, UUID submissionId) {

List<SubmissionAttachment> submissionAttachments = submissionAttachmentJpaRepository.findAllBySubmission(submission);
List<SubmissionAttachmentResponse> submissionAttachmentResponses = submissionAttachments.stream()
.map(SubmissionAttachmentResponse::from)
.map(attachment -> {
String downloadUrl = getDownloadUrlForAttachment(attachment);
return SubmissionAttachmentResponse.from(attachment, downloadUrl);
})
.toList();
return SubmissionResponse.from(submission, submissionAttachmentResponses);
}
Expand All @@ -87,7 +93,7 @@ public List<SubmissionCheck> checkAssignment(UUID userId, UUID assignmentId) {
.toList();
}

// 첨부 파일 혹은 링크 전체 조회 (학생)
// 첨부 파일 혹은 링크 전체 조회 (학생) - downloadUrl 포함
public List<SubmissionAttachmentResponse> findAllAssignmentStudent(UUID userId, UUID submissionId) {
Submission submission = findByIdOrThrow(submissionId);
UserEntity requestUser = userService.findById(userId);
Expand All @@ -99,11 +105,14 @@ public List<SubmissionAttachmentResponse> findAllAssignmentStudent(UUID userId,
List<SubmissionAttachment> attachments = submissionAttachmentJpaRepository.findAllBySubmission(submission);
return attachments.stream()
.filter(sa -> sa.getUser().getUserId().equals(userId))
.map(sa -> SubmissionAttachmentResponse.from(sa))
.map(attachment -> {
String downloadUrl = getDownloadUrlForAttachment(attachment);
return SubmissionAttachmentResponse.from(attachment, downloadUrl);
})
.toList();
}

// 첨부 파일 혹은 링크 전체 조회 (선생)
// 첨부 파일 혹은 링크 전체 조회 (선생) - downloadUrl 포함
public List<SubmissionAttachmentResponse> findAllAssignmentTeacher(UUID userId, UUID submissionId) {
UserEntity requestUser = userService.findById(userId);
if (!requestUser.getRole().equals(Role.TEACHER)) {
Expand All @@ -113,14 +122,25 @@ public List<SubmissionAttachmentResponse> findAllAssignmentTeacher(UUID userId,
Submission submission = findByIdOrThrow(submissionId);
List<SubmissionAttachment> attachments = submissionAttachmentJpaRepository.findAllBySubmission(submission);
return attachments.stream()
.map(sa -> SubmissionAttachmentResponse.from(sa))
.map(attachment -> {
String downloadUrl = getDownloadUrlForAttachment(attachment);
return SubmissionAttachmentResponse.from(attachment, downloadUrl);
})
.toList();
}

// 첨부파일 다운로드
public Resource downloadAttachment(SubmissionAttachment submissionAttachment) throws IOException {
String path = submissionAttachment.getValue();
return fileService.downloadFile(path);
// S3 다운로드 URL 생성 함수
public String getAttachmentDownloadUrl(UUID submissionAttachmentId) {
SubmissionAttachment attachment = findSubmissionAttachmentByIdOrThrow(submissionAttachmentId);
return getDownloadUrlForAttachment(attachment);
}

//
private String getDownloadUrlForAttachment(SubmissionAttachment attachment) {
if (attachment.getType() == hello.cluebackend.domain.submission.model.FileType.FILE) {
return fileService.getPresignedDownloadUrl(attachment.getValue());
}
return attachment.getValue();
}

public Submission findByIdOrThrow(UUID submissionId) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,18 +2,13 @@

import hello.cluebackend.domain.assignment.service.AssignmentCommandService;
import hello.cluebackend.domain.assignment.model.Assignment;
import hello.cluebackend.domain.assignment.model.AssignmentAttachment;
import hello.cluebackend.domain.assignment.exception.AccessDeniedException;
import hello.cluebackend.application.assignment.dto.response.AssignmentDto;
import hello.cluebackend.application.assignment.dto.response.GetAllAssignmentDto;
import hello.cluebackend.domain.classroomuser.service.ClassroomUserService;
import hello.cluebackend.application.user.dto.oauth2.CustomOAuth2User;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
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.security.core.annotation.AuthenticationPrincipal;
import org.springframework.web.bind.annotation.GetMapping;
Expand All @@ -23,8 +18,6 @@

import hello.cluebackend.application.assignment.dto.response.AssignmentAttachmentDto;

import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.util.List;
import java.util.UUID;

Expand Down Expand Up @@ -86,25 +79,16 @@ public ResponseEntity<List<AssignmentAttachmentDto>> getAttachments(
return ResponseEntity.ok(attachments);
}

// 첨부 파일 다운로드
@GetMapping("/{assignmentAttachmentId}/download")
public ResponseEntity<Resource> assignmentAttachmentDownload(
public ResponseEntity<String> assignmentAttachmentDownload(
@AuthenticationPrincipal CustomOAuth2User customOAuth2User,
@PathVariable UUID assignmentAttachmentId
) throws IOException {
AssignmentAttachment assignmentAttachment = assignmentCommandService.findAssignmentAttachmentByIdOrderThrow(customOAuth2User.getUserId(),assignmentAttachmentId);
Resource resource = assignmentCommandService.downloadAttachment(assignmentAttachment);

String original = assignmentAttachment.getOriginalFileName();
String contentType = assignmentAttachment.getContentType();
MediaType mediaType = (contentType != null) ? MediaType.parseMediaType(contentType) : MediaType.ALL;
) {
String downloadUrl = assignmentCommandService.getAttachmentDownloadUrl(
customOAuth2User.getUserId(),
assignmentAttachmentId
);

return ResponseEntity.ok()
.contentType(mediaType)
.header(HttpHeaders.CONTENT_DISPOSITION, ContentDisposition.attachment()
.filename(original, StandardCharsets.UTF_8)
.build()
.toString())
.body(resource);
return ResponseEntity.ok(downloadUrl);
}
}
Loading