投稿の作成+ファイルのアップロード(SpringBoot+JPA+AWS 3)
この記事では、投稿の作成+ファイルのアップロード(Awss 3)について説明します.🕌 Post
まず、投稿を生成するには、フロントからタイトル、コンテンツ、カテゴリ名、画像ファイルを取得する必要があります.
PostRequest 画像ファイルは PostController
筆者使用
画像ファイルを転送する場合、 PostService
まず、上の
ここで重要なのは、筆者が使用している
uploadImages() まず使用 PostImage
ファイルの内容タイプ=multiparty/form-data
残りは
レスポンス結果
PostUpdateRequest 修正時は、上記削除した画像経路以外の投稿画像経路を PostController投稿の修正は投稿の生成よりも難しい.
プレイヤー情報を探す
ユーザーと投稿の関係を検索します(変更権限があるかどうか)
ファイルの追加時に、ファイルをアップロード
JPAの使用 validateDeletedImages()・ uploadPostImages()
既存に2つのファイルが格納されている場合、1つの画像パスを入れて削除し、2つのファイルを入れるだけで、合計3つの画像パスを返す必要があります. 結果画面
@Getter
@NoArgsConstructor(access = PROTECTED)
@Entity
@EntityListeners(AuditListener.class)
public class Post implements Auditable {
@Id
@GeneratedValue(strategy = IDENTITY)
@Column(name = "post_id")
private Long id;
@Column(nullable = false)
private String title;
@Column(nullable = false, length = 1000)
private String content;
@Embedded
private TimeEntity timeEntity;
@ColumnDefault("0")
@Column(name = "view_count",nullable = false)
private Integer viewCount;
@ManyToOne(fetch = LAZY)
@JoinColumn(name = "user_id")
private User user;
@ManyToOne(fetch = LAZY,cascade = CascadeType.PERSIST)
@JoinColumn(name = "post_category_id")
private PostCategory postCategory;
@OneToMany(mappedBy = "post", orphanRemoval = true)
private List<Comment> comments = new ArrayList<>();
@OneToMany(mappedBy = "post", orphanRemoval = true)
private List<PostLike> postLikes = new ArrayList<>();
@OneToMany(mappedBy = "post", orphanRemoval = true)
private List<Scrap> scraps = new ArrayList<>();
@OneToMany(mappedBy = "post", orphanRemoval = true)
private List<PostImage> postImages = new ArrayList<>();
@Override
public void setTimeEntity(TimeEntity timeEntity) {
this.timeEntity = timeEntity;
}
@Builder
public Post(String title, String content, Integer viewCount, User user, List<Comment> comments, PostCategory postCategory) {
this.title = title;
this.content = content;
this.viewCount = viewCount;
this.user = user;
this.comments = comments;
this.postCategory = postCategory;
}
/**
* 생성 메서드
*/
public static Post createPost(String title, String content, User user, PostCategory postCategory){
return Post.builder()
.title(title)
.content(content)
.user(user)
.postCategory(postCategory)
.build();
}
public void updatePost(String title, String content) {
this.title = title;
this.content = content;
}
/**
* 초기화 값이 DB 에 추가되지 않는 오류가 있어서
* persist 하기 전에 초기화
*/
@PrePersist
public void prePersistCount(){
this.viewCount = this.viewCount == null ? 0 : this.viewCount;
}
}
Post
エンティティについては前述のとおり、詳細な説明を省略する.まず、投稿を生成するには、フロントからタイトル、コンテンツ、カテゴリ名、画像ファイルを取得する必要があります.
@ApiModel(description = "게시글 생성 요청 데이터 모델")
@Getter
@Setter
@NoArgsConstructor
public class PostRequest {
@ApiModelProperty(value = "게시글 제목", example = "모아모아 화이팅!", required = true)
private String title;
@ApiModelProperty(value = "게시글 내용", example = "무야호", required = true)
private String content;
@ApiModelProperty(value = "커뮤니티 게시글의 카테고리 이름", example = "모아모아", required = true)
private String categoryName;
@ApiModelProperty(value = "이미지 파일", required = false)
private List<MultipartFile> imageFiles = new ArrayList<>();
}
List
複数のファイルを受信する.@ApiOperation(value = "게시글 생성", notes = "Form Data 값을 받아와서 글을 생성하는 API",
consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
@ApiResponses({
@ApiResponse(responseCode = "200", description = "해당 게시글이 정상적으로 생성된 경우"),
@ApiResponse(responseCode = "404", description = "회원의 Id를 찾지 못한 경우")
})
@PostMapping
public PostCreateResponse createPost(@ModelAttribute PostRequest request){
return postService.createPost(request);
}
筆者使用
Swagger
行いAPI
文書化.画像ファイルを転送する場合、
FormData
方式で転送する必要があり、Jsonタイプのタイトル、内容、カテゴリ名を一緒に送信するので、@RequestPart
受信は可能ですが、明確には見えませんSwagger
ということで使用しました@ModelAttribut
@Transactional
public PostCreateResponse createPost(PostRequest postRequest) {
PostCategory postCategory = postCategoryRepository.findByCategoryName(postRequest.getCategoryName())
.orElseGet(() -> PostCategory.createCategory(postRequest.getCategoryName()));
User user = userUtil.findCurrent();
Post post = postRepository.save(Post.createPost(postRequest.getTitle(), postRequest.getContent(), user, postCategory));
List<String> postImages = uploadPostImages(postRequest, post);
return new PostCreateResponse(post.getId(), "게시글 작성이 완료되었습니다.", postImages);
}
まず、上の
postCategoryRepository.findByCategoryName()
投稿にカテゴリが存在するかどうかを確認し、存在しない場合はカテゴリインポートロジックを再生成し、上のコードは再変更されます.(DB
で予め作成・使用される)ここで重要なのは、筆者が使用している
JWT
は、ユーザの情報を単独で伝達するのではなく、HttpHeader
中AccessToken
から情報を受信し、それを用いてユーザの情報を検証し、検証の結果をSecurityContextに含めることである.userUtil.findCurrent()
受け取った価格からRequest
の投稿が生成されます. private List<String> uploadPostImages(PostRequest postRequest, Post post) {
return postRequest.getImageFiles().stream()
.map(image -> s3Uploader.upload(image, "post"))
.map(url -> createPostImage(post, url))
.map(postImage -> postImage.getImageUrl())
.collect(Collectors.toList());
}
createPostImage() private PostImage createPostImage(Post post, String url) {
return postImageRepository.save(PostImage.builder()
.imageUrl(url)
.storeFilename(StringUtils.getFilename(url))
.post(post)
.build());
}
PostRepository.save()
受信した画像ファイルを予め設定されたstream()
パスに保存した後、S3
エンティティを生成し、その画像パスを戻すPostImage
@Entity
@Getter
@NoArgsConstructor(access = PROTECTED)
@EntityListeners(AuditListener.class)
public class PostImage implements Auditable {
@Id
@GeneratedValue(strategy = IDENTITY)
@Column(name = "post_image_id")
private Long id;
private String imageUrl;
private String storeFilename;
@Embedded
private TimeEntity timeEntity;
@ManyToOne
@JoinColumn(name = "post_id")
private Post post;
@Builder
public PostImage(String imageUrl, String storeFilename, Post post) {
this.imageUrl = imageUrl;
this.storeFilename = storeFilename;
this.post = post;
}
@Override
public void setTimeEntity(TimeEntity timeEntity) {
this.timeEntity = timeEntity;
}
}
PostmanテストList
と指定されている場合は、上記@ModelAttribute
方式にてkey+value方式で送信する.ファイルの内容タイプ=multiparty/form-data
残りは
form-data
方式で送信するので、上記の設定を行いました.投稿の変更🏡
@ApiModel(description = "게시글 수정 요청 데이터 모델")
@Getter
@Setter
@NoArgsConstructor
@AllArgsConstructor
public class PostUpdateRequest {
@ApiModelProperty(value = "게시글 PK", example = "1", required = true)
@NotNull
private Long postId;
@ApiModelProperty(value = "게시글 제목", example = "모아모아 화이팅!")
private String title;
@ApiModelProperty(value = "게시글 내용", example = "무야호")
private String content;
@ApiModelProperty(value = "삭제한 이미지 경로를 제외한 남아있는 게시글 이미지 경로")
private List<String> saveImageUrl = new ArrayList<>();
@ApiModelProperty(value = "게시글 이미지", required = false)
private List<MultipartFile> imageFiles = new ArrayList<>();
}
Json
に送信する. @ApiOperation(value = "게시글 수정", notes = "Request Body 값을 받아와서 글을 수정하는 API")
@ApiResponses({
@ApiResponse(responseCode = "200", description = "해당 게시글이 정상적으로 수정된 경우"),
@ApiResponse(responseCode = "404", description = "회원 OR 게시글의 Id를 찾지 못한 경우")
})
@PatchMapping
public PostUpdateResponse updatePost(@ModelAttribute PostUpdateRequest request) {
return postService.updatePost(request);
}
postService@Transactional
public PostUpdateResponse updatePost(PostUpdateRequest request) {
User user = userUtil.findCurrent();
Post post = postRepository.findByIdAndUser(request.getPostId(), user.getId())
.orElseThrow(() -> new CustomException(ErrorCode.NOT_FOUND_POST));
validateDeletedImages(request);
uploadPostImages(request, post);
List<String> saveImages = getSaveImages(request);
post.updatePost(request.getTitle(), request.getContent());
return new PostUpdateResponse(post.getId(), "게시글 변경이 완료되었습니다.", saveImages);
}
プレイヤー情報を探す
ユーザーと投稿の関係を検索します(変更権限があるかどうか)
List
受信した画像経路が記憶されている画像経路と一致しない場合は、全て削除ファイルの追加時に、ファイルをアップロード
Request
で作成S3
PostImage
テーブルに格納されている画像パスを抽出するJPAの使用
PostImage
機能変更投稿변경감지
/**
* @Request로 받아온 이미지 경로랑 저장 되어있던 이미지 경로랑 일치하지 않는다면 모두 삭제
*/
private void validateDeletedImages(PostUpdateRequest request) {
postImageRepository.findBySavedImageUrl(request.getPostId()).stream()
.filter(image -> !request.getSaveImageUrl().stream().anyMatch(Predicate.isEqual(image.getImageUrl())))
.forEach(url -> {
postImageRepository.delete(url);
s3Uploader.deleteImage(url.getImageUrl());
});
}
update
を使用して、stream
受信した画像経路とrequest
テーブルに格納されている経路が一致しているかどうかを検証した後、一致しなければ全て削除します. /**
* S3에 업로드 및 PostImage 생성
*/
private void uploadPostImages(PostUpdateRequest request, Post post) {
request.getImageFiles()
.stream()
.forEach(file -> {
String url = s3Uploader.upload(file, "post");
createPostImage(post, url);
});
}
getSaveImages() /**
* PostImage 테이블에 저장 되어있는 이미지 경로를 추출
*/
private List<String> getSaveImages(PostUpdateRequest request) {
return postImageRepository.findBySavedImageUrl(request.getPostId())
.stream()
.map(image -> image.getImageUrl())
.collect(Collectors.toList());
}
PostmanテストS 3の設定やロジックについては他のブログでいろいろありますが、参考にしてください...🍎
Reference
この問題について(投稿の作成+ファイルのアップロード(SpringBoot+JPA+AWS 3)), 我々は、より多くの情報をここで見つけました https://velog.io/@do-hoon/게시글-생성-파일-업로드SpringBoot-JPA-AWS-S3テキストは自由に共有またはコピーできます。ただし、このドキュメントのURLは参考URLとして残しておいてください。
Collection and Share based on the CC Protocol