Skip to content

Commit

Permalink
Feat: 이미지 업로드 기능
Browse files Browse the repository at this point in the history
  • Loading branch information
cmsong111 committed Apr 1, 2024
1 parent e313cec commit f2c5fb5
Show file tree
Hide file tree
Showing 8 changed files with 179 additions and 1 deletion.
3 changes: 3 additions & 0 deletions .github/workflows/Build&Test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,9 @@ jobs:
- name: Setup Gradle
uses: gradle/actions/setup-gradle@417ae3ccd767c252f5661f1ace9f835f9654f2b5 # v3.1.0

- name: Set Environment Variables
run: echo "IMGBB_API_KEY=${{ secrets.IMGBB_API_KEY }}" >> $GITHUB_ENV

- name: Test with Gradle
run: ./gradlew --info test

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,8 @@ public enum CustomErrorCode {
ROLE_NOT_MATCH(HttpStatus.BAD_REQUEST, "권한이 일치하지 않습니다."),
FOOD_TRUCK_MENU_NOT_FOUND(HttpStatus.BAD_REQUEST, "푸드트럭 메뉴를 찾을 수 없습니다."),
FOOD_TRUCK_IS_ALREADY_OPERATING(HttpStatus.BAD_REQUEST, "푸드트럭이 이미 운영중입니다."),
FOOD_TRUCK_IS_NOT_OPERATING(HttpStatus.BAD_REQUEST, "푸드트럭이 운영중이 아닙니다.");
FOOD_TRUCK_IS_NOT_OPERATING(HttpStatus.BAD_REQUEST, "푸드트럭이 운영중이 아닙니다."),
IMAGE_UPLOAD_FAILED(HttpStatus.BAD_REQUEST, "이미지 업로드에 실패했습니다.");


private final HttpStatus status;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
package ac.kr.deu.connect.luck.image;

import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.tags.Tag;
import lombok.AllArgsConstructor;
import org.springframework.http.MediaType;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestPart;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile;

@RestController
@Tag(name = "Image", description = "Image API")
@RequestMapping("/api/image")
@AllArgsConstructor
public class ImageRestController {

private final ImageUploader imageUploader;

@PostMapping(consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
public String uploadImage(
@Parameter(description = "multipart/form-data 형식의 이미지 리스트를 input으로 받습니다. 이때 key 값은 image 입니다.")
@RequestPart("image") MultipartFile multipartFile) {
return imageUploader.uploadImage(multipartFile).getData().getUrl();
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
package ac.kr.deu.connect.luck.image;

import lombok.Getter;
import lombok.Setter;
import lombok.ToString;

@Getter
@Setter
@ToString
public class ImageUploadResponse {
private Data data;
private boolean success;
private int status;

@Getter
@Setter
@ToString
public static class Data {
private String id;
private String title;
private String urlViewer;
private String url;
private String displayUrl;
private String width;
private String height;
private String size;
private String time;
private String expiration;
private ImageInfo image;
private ImageInfo thumb;
private ImageInfo medium;
private String deleteUrl;
}

@Getter
@Setter
@ToString
public static class ImageInfo {
private String filename;
private String name;
private String mime;
private String extension;
private String url;
}
}
58 changes: 58 additions & 0 deletions src/main/java/ac/kr/deu/connect/luck/image/ImageUploader.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
package ac.kr.deu.connect.luck.image;

import ac.kr.deu.connect.luck.exception.CustomErrorCode;
import ac.kr.deu.connect.luck.exception.CustomException;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Component;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
import org.springframework.web.client.RestTemplate;
import org.springframework.web.multipart.MultipartFile;

import java.util.Base64;

@Component
@Slf4j
public class ImageUploader {

@Value("${imgbb.api-key}")
private String API_KEY;

private String BASE_URL = "https://api.imgbb.com/1/upload";

private Long EXPIRATION = 15552000L;

private RestTemplate restTemplate = new RestTemplate();


public ImageUploadResponse uploadImage(MultipartFile file) {
try {
byte[] image = file.getBytes();
String base64Image = Base64.getEncoder().encodeToString(image);

String apiUrl = String.format("%s?key=%s&expiration=%d", BASE_URL, API_KEY, EXPIRATION);

HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED);

MultiValueMap<String, String> bodyMap = new LinkedMultiValueMap<>();
bodyMap.add("image", base64Image);

HttpEntity<MultiValueMap<String, String>> request = new HttpEntity<>(bodyMap, headers);

ResponseEntity<ImageUploadResponse> response = restTemplate.postForEntity(apiUrl, request, ImageUploadResponse.class);

log.info("Image upload response: {}", response.getBody());
log.info("Image upload URL: {}", response.getBody().getData().getUrl());
return response.getBody();
} catch (Exception e) {
log.error("Image upload failed: {}", e.getMessage());
throw new CustomException(CustomErrorCode.IMAGE_UPLOAD_FAILED);
}
}
}
8 changes: 8 additions & 0 deletions src/main/resources/application.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,11 @@ spring:
mode: embedded
encoding: utf-8

servlet:
multipart:
max-file-size: 32MB
max-request-size: 32MB


logging:
level:
Expand All @@ -27,3 +32,6 @@ server:
encoding:
charset: UTF-8
force-response: true

imgbb:
api-key: ${IMGBB_API_KEY}
35 changes: 35 additions & 0 deletions src/test/java/ac/kr/deu/connect/luck/image/ImageUploaderTest.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
package ac.kr.deu.connect.luck.image;

import ac.kr.deu.connect.luck.image.ImageUploader;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.mock.web.MockMultipartFile;

import java.io.FileInputStream;

import static org.junit.jupiter.api.Assertions.*;


@SpringBootTest
class ImageUploaderTest {

@Autowired
private ImageUploader imageUploader;

@Test
void uploadImage() throws Exception {
// Given
final String filePath = "src/test/resources/test.jpg";
FileInputStream fis = new FileInputStream(filePath);
MockMultipartFile file = new MockMultipartFile("file", fis);

// When
ImageUploadResponse imageUrl = imageUploader.uploadImage(file);

// Then
assertEquals(200, imageUrl.getStatus());
assertNotNull(imageUrl.getData().getUrl());
}

}
Binary file added src/test/resources/test.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.

0 comments on commit f2c5fb5

Please sign in to comment.