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
Binary file added src/main/.DS_Store
Binary file not shown.
2 changes: 1 addition & 1 deletion src/main/java/codesquad/ImsApplication.java
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ public static void main(String[] args) {


/**
* Todo : Step5 ~ing
* Todo : Step6 ~ing 파일첨부하기
* 1. html 자원들 중복 제거하면서 스타일이 제대로 안나오는 부분이 있다, 추후 중복 제거 다시해서 레이아웃이 제대로 나오도록
* 2. 회원가입/가입정보수정 시 유효하지 않은 값 입력하고 submit 시 어떤 경우에 alert, 어떤 경우에 오류페이지 나오는지 확인 후 개선
*/
57 changes: 57 additions & 0 deletions src/main/java/codesquad/domain/Attachment.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
package codesquad.domain;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.multipart.MultipartFile;
import support.domain.AbstractEntity;

import javax.persistence.*;

@Entity
public class Attachment extends AbstractEntity {
private static final Logger log = LoggerFactory.getLogger(Attachment.class);

@Column
private String originalName;

@Column
private String extension;

@Column
private String convertedName;

@ManyToOne
@JoinColumn(foreignKey = @ForeignKey(name = "fk_attachment_uploader"))
private User uploader;

public Attachment() {

}

public static Attachment convertedAttachment(MultipartFile file, String convertedName) {
Attachment attachment = new Attachment();
attachment.originalName = file.getOriginalFilename();
attachment.convertedName = convertedName;
return attachment;
}

public void loadedBy(User uploader) {
this.uploader = uploader;
}

public String getOriginalName() {
return originalName;
}

public void setOriginalName(String originalName) {
this.originalName = originalName;
}

public String getConvertedName() {
return convertedName;
}

public void setConvertedName(String convertedName) {
this.convertedName = convertedName;
}
}
6 changes: 6 additions & 0 deletions src/main/java/codesquad/domain/AttachmentRepository.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
package codesquad.domain;

import org.springframework.data.jpa.repository.JpaRepository;

public interface AttachmentRepository extends JpaRepository<Attachment, Long> {
}
16 changes: 16 additions & 0 deletions src/main/java/codesquad/domain/issue/Comment.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import codesquad.CannotDeleteException;
import codesquad.UnAuthorizedException;
import codesquad.domain.Attachment;
import codesquad.domain.ContentType;
import codesquad.domain.DeleteHistory;
import codesquad.domain.User;
Expand Down Expand Up @@ -30,6 +31,9 @@ public class Comment extends AbstractEntity {
@JoinColumn(foreignKey = @ForeignKey(name = "fk_comment_issue"))
private Issue issue;

@OneToOne
private Attachment attachment;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

첨부 파일이 하나가 아니라 n개가 될 수도 있는데 그 부분까지 고려하면 좋을 것 같네요.


private boolean deleted = false;

public Comment() {
Expand Down Expand Up @@ -98,6 +102,14 @@ public void setIssue(Issue issue) {
this.issue = issue;
}

public Attachment getAttachment() {
return attachment;
}

public void setAttachment(Attachment attachment) {
this.attachment = attachment;
}

@Override
public String toString() {
return "Comment{" +
Expand All @@ -107,4 +119,8 @@ public String toString() {
", issue=" + issue +
'}';
}

public void attach(Attachment attachment) {
this.attachment = attachment;
}
}
34 changes: 34 additions & 0 deletions src/main/java/codesquad/service/AttachmentService.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
package codesquad.service;

import codesquad.domain.Attachment;
import codesquad.domain.AttachmentRepository;
import codesquad.domain.User;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import javax.persistence.EntityNotFoundException;

@Service
public class AttachmentService {
private static final Logger log = LoggerFactory.getLogger(AttachmentService.class);

@Resource(name = "attachmentRepository")
private AttachmentRepository attachmentRepository;

public Attachment add(Attachment attachment) {
return attachmentRepository.save(attachment);
}

@Transactional
public Attachment save(User loginUser, Attachment attachment) {
attachment.loadedBy(loginUser);
return add(attachment);
}

public Attachment findById(long id) {
return attachmentRepository.findById(id).orElseThrow(EntityNotFoundException::new);
}
}
9 changes: 9 additions & 0 deletions src/main/java/codesquad/service/CommentService.java
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package codesquad.service;

import codesquad.CannotDeleteException;
import codesquad.domain.Attachment;
import codesquad.domain.User;
import codesquad.domain.issue.Comment;
import codesquad.domain.issue.CommentRepository;
Expand Down Expand Up @@ -49,4 +50,12 @@ public Comment update(User loginUser, long id, String body) {
Comment comment = commentRepository.findById(id).orElseThrow(NoResultException::new);
return comment.update(loginUser, body);
}

@Transactional
public Comment uploadAttachment(User loginUser, long issueId, Attachment attachment) {
Issue issue = issueService.findById(issueId);
Comment comment = new Comment(loginUser, issue, attachment.getOriginalName());
comment.attach(attachment);
return add(comment);
}
}
3 changes: 0 additions & 3 deletions src/main/java/codesquad/service/IssueService.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,19 +2,16 @@

import codesquad.CannotDeleteException;
import codesquad.domain.*;
import codesquad.domain.issue.Comment;
import codesquad.domain.issue.Issue;
import codesquad.domain.issue.IssueRepository;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.data.domain.Pageable;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.bind.annotation.RequestBody;

import javax.annotation.Resource;
import javax.persistence.EntityNotFoundException;
import javax.validation.Valid;
import java.util.List;

@Service
Expand Down
68 changes: 68 additions & 0 deletions src/main/java/codesquad/web/AttachmentController.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
package codesquad.web;

import java.nio.file.*;
import java.util.UUID;

import codesquad.domain.Attachment;
import codesquad.domain.User;
import codesquad.security.LoginUser;
import codesquad.service.AttachmentService;
import codesquad.service.CommentService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.core.io.FileSystemResource;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.multipart.MultipartFile;

import javax.annotation.Resource;

@Controller
@RequestMapping("/issues/{issueId}/attachments")
public class AttachmentController {
private static final Logger log = LoggerFactory.getLogger(AttachmentController.class);

@Resource(name = "attachmentService")
private AttachmentService attachmentService;

@Resource(name = "commentService")
private CommentService commentService;

@PostMapping("")
public String upload(@LoginUser User loginUser, @PathVariable long issueId, MultipartFile file) throws Exception {
log.debug("original file name: {}", file.getOriginalFilename());
log.debug("contenttype: {}", file.getContentType());

String url = "/Users/zingo/Documents/work";
String convertedName = UUID.randomUUID().toString();

Files.copy(file.getInputStream(), Paths.get(url, convertedName), StandardCopyOption.REPLACE_EXISTING);
commentService.uploadAttachment(loginUser, issueId, attachmentService.save(loginUser, Attachment.convertedAttachment(file, convertedName)));

return "redirect:/issues/{issueId}";
}

@GetMapping(value = "/{commentId}", produces = {MediaType.IMAGE_JPEG_VALUE, MediaType.IMAGE_PNG_VALUE, MediaType.IMAGE_JPEG_VALUE})
public ResponseEntity<FileSystemResource> download(@PathVariable long commentId) throws Exception {
Attachment attachment = attachmentService.findById(commentId);
Path path = Paths.get("/Users/zingo/Documents/work/" + attachment.getConvertedName());
log.debug("***** path : {}", path.toString());
FileSystemResource resource = new FileSystemResource(path);

//TODO: OCTET_STREAM 써도 무방한가?
HttpHeaders header = new HttpHeaders();
header.setContentType(MediaType.APPLICATION_OCTET_STREAM);
header.set(HttpHeaders.CONTENT_DISPOSITION,
"attachment; filename=" + java.net.URLEncoder.encode(attachment.getOriginalName(), "UTF-8"));
header.setContentLength(resource.contentLength());

return new ResponseEntity<>(resource, header, HttpStatus.OK);
}
}
Binary file added src/main/resources/.DS_Store
Binary file not shown.
26 changes: 20 additions & 6 deletions src/main/resources/templates/issue/show.html
Original file line number Diff line number Diff line change
Expand Up @@ -130,24 +130,31 @@ <h2 class="mdl-card__title-text">{{subject}}</h2>
<div class="mdl-color-text--grey-700 mdl-card__supporting-text">
<p>{{comment}}</p>
</div>
<form action="/issue/1/uploadFile" enctype="multipart/form-data" method="POST">
<div style="margin:10px;">
<input type="file" name="file" id="file"/>
<button class="mdl-button" type="submit">upload file</button>
</div>
</form>
<!--<form action="/issues/{{id}}/attachments" enctype="multipart/form-data" method="POST">-->
<!--<div style="margin:10px;">-->
<!--<input type="file" name="file" id="file"/>-->
<!--<button class="mdl-button" type="submit">upload file</button>-->
<!--</div>-->
<!--</form>-->

<div class="mdl-color-text--primary-contrast mdl-card__supporting-text comments">
{{# comments}}
<!-- comments start -->


<comment class="comment mdl-color-text--grey-700 comment-form" id="comment-{{id}}">
<header class="comment__header">
<img class="comment__avatar2" height="48" width="48"
src="https://avatars2.githubusercontent.com/u/520500?v=3&s=140" alt="javajigi">
<div class="comment__author">
<strong>
<a>{{writer.userId}}</a>
{{^ attachment}}
<span>{{comment}}</span>
{{/ attachment}}
{{# attachment}}
<span>{{comment}} <a href="/issues/{{issue.id}}/attachments/{{id}}">[DOWNLOAD]</a></span>
{{/ attachment}}
</strong>
</div>
<div class="comment-menu">
Expand All @@ -168,7 +175,14 @@ <h2 class="mdl-card__title-text">{{subject}}</h2>
<!-- comments end -->
{{/ comments}}
</div>
<form action="/issues/{{issue.id}}/attachments" enctype="multipart/form-data" method="POST">
<div style="margin:10px;">
<input type="file" name="file" id="file"/>
<button class="mdl-button" type="submit">upload file</button>
</div>
</form>
<div class="mdl-color-text--primary-contrast mdl-card__supporting-text new-comment">

<form action="/api/issues/{{issue.id}}/comments" method="POST">
<div class="mdl-textfield mdl-js-textfield mdl-textfield--floating-label">
<textarea rows=5 class="mdl-textfield__input" id="comment" name="comment"></textarea>
Expand Down
35 changes: 35 additions & 0 deletions src/test/java/codesquad/web/AttachmentControllerTest.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
package codesquad.web;

import static org.junit.Assert.*;

import org.junit.Test;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.core.io.ClassPathResource;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.util.MultiValueMap;
import support.test.AcceptanceTest;
import support.test.HtmlFormDataBuilder;

public class AttachmentControllerTest extends AcceptanceTest {
private static final Logger log = LoggerFactory.getLogger(AttachmentControllerTest.class);

@Test
public void download() throws Exception {
ResponseEntity<String> result = template.getForEntity("/attachments/1", String.class);
assertEquals(HttpStatus.OK, result.getStatusCode());
log.debug("body : {}", result.getBody());
}

@Test
public void upload() throws Exception {
HttpEntity<MultiValueMap<String, Object>> request = HtmlFormDataBuilder
.multipartFormData()
.addParameter("file", new ClassPathResource("logback.xml"))
.build();
ResponseEntity<String> result = template.postForEntity("/attachments", request, String.class);
assertEquals(HttpStatus.FOUND, result.getStatusCode());
}
}