Skip to content

Commit

Permalink
Merge pull request #30 from Coastee/feat/#28-image-storage
Browse files Browse the repository at this point in the history
[feat/#28-image-storage] 이미지 삭제 및 저장
  • Loading branch information
5jisoo authored Jan 21, 2025
2 parents 44bc862 + 0f974a5 commit d9d99c2
Show file tree
Hide file tree
Showing 8 changed files with 129 additions and 10 deletions.
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
### data ###
docker/redis

### STS ###
.apt_generated
.classpath
Expand Down
4 changes: 4 additions & 0 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,10 @@ dependencies {
implementation 'org.springframework.boot:spring-boot-starter-security'
testImplementation 'org.springframework.security:spring-security-test'

// azure blob storage
implementation 'com.azure.spring:spring-cloud-azure-dependencies:5.19.0'
implementation 'com.azure.spring:spring-cloud-azure-starter-storage-blob:5.19.0'

// jasypt encryption
implementation "com.github.ulisesbocchio:jasypt-spring-boot-starter:3.0.5"

Expand Down
27 changes: 27 additions & 0 deletions src/main/java/com/coastee/server/image/domain/DirName.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
package com.coastee.server.image.domain;

import com.coastee.server.auth.domain.Authority;
import lombok.Getter;
import lombok.RequiredArgsConstructor;

import java.util.Arrays;

@Getter
@RequiredArgsConstructor
public enum DirName {
USER("user"), POST("post"), CHAT("chat");

private final String code;

@Override
public String toString() {
return this.code;
}

public static Authority of(final String code) {
return Arrays.stream(Authority.values())
.filter(r -> r.getCode().equals(code.toUpperCase()))
.findAny()
.orElse(null);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
package com.coastee.server.image.service;

import com.azure.core.util.BinaryData;
import com.azure.core.util.Context;
import com.azure.storage.blob.BlobClient;
import com.azure.storage.blob.BlobContainerClient;
import com.azure.storage.blob.models.BlobHttpHeaders;
import com.azure.storage.blob.models.BlobRequestConditions;
import com.azure.storage.blob.options.BlobParallelUploadOptions;
import com.coastee.server.global.apipayload.exception.GeneralException;
import com.coastee.server.image.domain.DirName;
import lombok.RequiredArgsConstructor;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;

import java.io.IOException;

import static com.coastee.server.global.apipayload.code.status.ErrorStatus.IO_EXCEPTION;

@Service
@RequiredArgsConstructor
public class BlobStorageService {
private final BlobContainerClient blobContainerClient;

@Value("${spring.cloud.azure.storage.blob.account-name}")
private String account;

@Value("${spring.cloud.azure.storage.blob.container-name}")
private String container;

public String upload(
final MultipartFile file,
final DirName imageDir,
final Long id
) {
String fileName = imageDir + "/" + id;

BlobClient blobClient = blobContainerClient.getBlobClient(fileName);
BlobParallelUploadOptions uploadOptions = getUploadOptions(file);
blobClient.uploadWithResponse(uploadOptions, null, Context.NONE);

return String.format("https://%s.blob.core.windows.net/%s/%s/%s", account, container, imageDir, id);
}

public void delete(
final DirName imageDir,
final Long id
) {
String fileName = imageDir + "/" + id;
BlobClient blobClient = blobContainerClient.getBlobClient(fileName);
blobClient.delete();
}

private BlobParallelUploadOptions getUploadOptions(final MultipartFile file) {
try {
BlobHttpHeaders jsonHeaders = new BlobHttpHeaders()
.setContentType(file.getContentType());
BinaryData data = BinaryData.fromStream(file.getInputStream(), file.getSize());
return new BlobParallelUploadOptions(data)
.setRequestConditions(new BlobRequestConditions())
.setHeaders(jsonHeaders);
} catch (IOException e) {
throw new GeneralException(IO_EXCEPTION);
}
}
}
22 changes: 20 additions & 2 deletions src/main/java/com/coastee/server/user/domain/Experience.java
Original file line number Diff line number Diff line change
@@ -1,16 +1,34 @@
package com.coastee.server.user.domain;

import com.coastee.server.global.BaseEntity;
import jakarta.persistence.*;
import lombok.Builder;
import lombok.Getter;
import lombok.NoArgsConstructor;

import java.io.Serializable;
import java.time.LocalDateTime;

import static jakarta.persistence.FetchType.LAZY;
import static lombok.AccessLevel.PROTECTED;

@Getter
public class Experience implements Serializable {
@Entity
@NoArgsConstructor(access = PROTECTED)
public class Experience extends BaseEntity {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;

@ManyToOne(fetch = LAZY)
@JoinColumn(name = "user_id")
private User user;

private String title;

private LocalDateTime startDate;

private LocalDateTime endDate;

private String content;

@Builder(builderMethodName = "of")
Expand Down
5 changes: 0 additions & 5 deletions src/main/java/com/coastee/server/user/domain/User.java
Original file line number Diff line number Diff line change
Expand Up @@ -36,9 +36,6 @@ public class User extends BaseEntity {
@JdbcTypeCode(SqlTypes.ARRAY)
private List<String> urlList = new ArrayList<>();

@JdbcTypeCode(SqlTypes.JSON)
private List<Experience> experienceList = new ArrayList<>();

@Enumerated(STRING)
private SocialType socialType;

Expand All @@ -52,7 +49,6 @@ public User(
final String profileImage,
final String refreshToken,
final List<String> urlList,
final List<Experience> experienceList,
final SocialType socialType,
final String socialId
) {
Expand All @@ -62,7 +58,6 @@ public User(
this.profileImage = profileImage;
this.refreshToken = refreshToken;
this.urlList = urlList;
this.experienceList = experienceList;
this.socialType = socialType;
this.socialId = socialId;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,6 @@ public class ProfileResponse {
private String bio;
private String profileImage;
private List<String> urlList;
private List<Experience> experienceList;

public static ProfileResponse from(final User user) {
return new ProfileResponse(
Expand All @@ -29,8 +28,7 @@ public static ProfileResponse from(final User user) {
user.getHeadline(),
user.getBio(),
user.getProfileImage(),
user.getUrlList(),
user.getExperienceList()
user.getUrlList()
);
}
}
7 changes: 7 additions & 0 deletions src/main/resources/application.yml
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,13 @@ spring:
url: ENC(YbE5zqnIUB6ekwgzEtUY9+vfU13OSev9N00+dIaSK0JpMQ7ajpK8EcIBlUFg+jdNd0Q3r9ow6e8io/hEtii5z3YN4vpV6jQrKcm4i3a6eqE/FtwTUT0zD/BpWNGA5MG4h3SWmWzWCzG9eLEoUw2ANbxksDXsxJ3xx7mJ+6kAHt0=)
username: ENC(1a0kqqs8WICexcTbsqfqmg==)
password: ENC(BvKDYlNIWK7fxHjiuEEQpGc+oDqD9XS3vz2d/EWwqok=)
cloud:
azure:
storage:
blob:
account-name: coaster
container-name: coaster-image
account-key: ENC(sSEeJWspfQCXTqUPvyI5Nsz2cPuVUl9aAVUJgDjccwU0WpwojTB6EZknus7474nCgWPxtY7T4lyavN4UUxUaSsesjLQgn6o7xSCv+rdVpbkJaK1r56lq+MJqPucJ+68c2Wx8MEqGo7w=)
jwt:
secretKey: ENC(bekmp+cZY7Qvbii3ldKJxdKF1Qx+O1J0wmlrxf9GbygY/aIGNzIfIwo0g56qzJ5WqM6FWbg/UjM3+BLjQJ/NHD1G0T1OcUW36rZ2xAI81AfN23y4PSV1J+5ybf/s98D975u/nWr+f3JRbPrpowqkj2R6XW6Vrm4KXqLeDXfPetAaZFYA0rK6XMbit6J/nya7)
jasypt:
Expand Down

0 comments on commit d9d99c2

Please sign in to comment.