File 받기/보내기 기본적 구현 (REST API)
DB에 파일을 저장(업로드)하는 방식 JPA 어노테이션으로 이미지파일 자체를 @Lob
어노테이션을 통해 Entity 필드를 만든다.
🤔 하지만! 현업에서는 DB 다운로드한 바이트(이미지 파일) 자체를 저장하지 않는다. url 주소를 저장하고 따로 파일을 저장하는 저장소에서 불러오는 방식을 사용한다. 요즘에는 AWS S3에서 파일을 저장하고 해당 S3의 파일 url을 불러오는 방식이다!
Entity : ImageData
@Entity
@Table(name = "ImageData")
@Getter
@NoArgsConstructor(access = AccessLevel.PROTECTED)
public class ImageData {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
private String type;
@Lob
@Column(name = "imagedata", length = 1000)
private byte[] imageData;
@Builder
public ImageData(String name, String type, byte[] imageData) {
this.name = name;
this.type = type;
this.imageData = imageData;
}
}
Controller
@RestController
@RequiredArgsConstructor
@RequestMapping("/image")
public class StorageController {
final private StorageService storageService;
// 업로드
@PostMapping
public ResponseEntity<?> uploadImage(@RequestParam("image") MultipartFile file) throws IOException {
String uploadImage = storageService.uploadImage(file);
return ResponseEntity.status(HttpStatus.OK)
.body(uploadImage);
}
// 다운로드
@GetMapping("/{fileName}")
public ResponseEntity<?> downloadImage(@PathVariable("fileName") String fileName) {
byte[] downloadImage = storageService.downloadImage(fileName);
return ResponseEntity.status(HttpStatus.OK)
.contentType(MediaType.valueOf("image/png"))
.body(downloadImage);
}
}
Service
@Slf4j
@Service
@RequiredArgsConstructor
public class StorageService {
private final StorageRepository storageRepository;
public String uploadImage(MultipartFile file) throws IOException {
log.info("upload file: {}", file);
ImageData imageData = storageRepository.save(
ImageData.builder()
.name(file.getOriginalFilename())
.type(file.getContentType())
.imageData(ImageUtils.compressImage(file.getBytes()))
.build());
if (imageData != null) {
log.info("imageData: {}", imageData);
return "file uploaded successfully : " + file.getOriginalFilename();
}
return null;
}
// 이미지 파일로 압축하기
public byte[] downloadImage(String fileName) {
ImageData imageData = storageRepository.findByName(fileName)
.orElseThrow(RuntimeException::new);
log.info("download imageData: {}", imageData);
return ImageUtils.decompressImage(imageData.getImageData());
}
}
Repository
public interface StorageRepository extends JpaRepository<ImageData, Long> {
Optional<ImageData> findByName(String fileName);
}
Utils : ImageUtils
public class ImageUtils {
public static byte[] compressImage(byte[] data) {
Deflater deflater = new Deflater();
deflater.setLevel(Deflater.BEST_COMPRESSION);
deflater.setInput(data);
deflater.finish();
ByteArrayOutputStream outputStream = new ByteArrayOutputStream(data.length);
byte[] tmp = new byte[4*1024];
while (!deflater.finished()) {
int size = deflater.deflate(tmp);
outputStream.write(tmp, 0, size);
}
try {
outputStream.close();
} catch (Exception ignored) {
}
return outputStream.toByteArray();
}
public static byte[] decompressImage(byte[] data) {
Inflater inflater = new Inflater();
inflater.setInput(data);
ByteArrayOutputStream outputStream = new ByteArrayOutputStream(data.length);
byte[] tmp = new byte[4*1024];
try {
while (!inflater.finished()) {
int count = inflater.inflate(tmp);
outputStream.write(tmp, 0, count);
}
outputStream.close();
} catch (Exception ignored) {
}
return outputStream.toByteArray();
}
}
결과
POST - 업로드
localhost:8080/image
postman에서도 파일을 첨부해서 넣을 수 있다.
form-data
선택File
로 대체결과
DB에도 잘 저장되어 있는 것을 확인할 수 있었다.
GET - 다운로드
localhost:8080/image/black_horse_running-wallpaper-1680x1050.jpg
파일 이미지를 Body
를 통해 보여줌.
filePath를 DB에 저장하고 그 filePath(로컬에서 저장한 파일 경로)에서 파일을 다운로드하는 방식
Entity : FileData
@Entity
@Table(name = "FileData")
@Getter
@NoArgsConstructor(access = AccessLevel.PROTECTED)
public class FileData {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
private String type;
private String filePath;
@Builder
public FileData(String name, String type, String filePath) {
this.name = name;
this.type = type;
this.filePath = filePath;
}
}
Controller
@PostMapping("/fileSystem")
public ResponseEntity<?> uploadImageToFileSystem(@RequestParam("image") MultipartFile file) throws IOException {
String uploadImage = storageService.uploadImageToFileSystem(file);
return ResponseEntity.status(HttpStatus.OK)
.body(uploadImage);
}
@GetMapping("/fileSystem/{fileName}")
public ResponseEntity<?> downloadImageToFileSystem(@PathVariable("fileName") String fileName) throws IOException {
byte[] downloadImage = storageService.downloadImageFromFileSystem(fileName);
return ResponseEntity.status(HttpStatus.OK)
.contentType(MediaType.valueOf("image/png"))
.body(downloadImage);
}
Service
// 파일 경로 지정, 윈도우인 경우, '\' 이스케이프 2개 필요!
private final String FOLDER_PATH = "C:\\WebStudy\\WebDevelement\\SpringBoot\\storage-service\\files\\";
public String uploadImageToFileSystem(MultipartFile file) throws IOException {
log.info("upload file: {}", file.getOriginalFilename());
String filePath = FOLDER_PATH + file.getOriginalFilename();
FileData fileData = fileDataRepository.save(
FileData.builder()
.name(file.getOriginalFilename())
.type(file.getContentType())
.filePath(filePath)
.build()
);
// 파일 결로
file.transferTo(new File(filePath));
if (fileData != null) {
return "file uploaded successfully! filePath : " + filePath;
}
return null;
}
public byte[] downloadImageFromFileSystem(String fileName) throws IOException {
FileData fileData = fileDataRepository.findByName(fileName)
.orElseThrow(RuntimeException::new);
String filePath = fileData.getFilePath();
log.info("download fileData: {}", fileData);
log.info("download filePath: {}", filePath);
return Files.readAllBytes(new File(filePath).toPath());
}
Repository
public interface FileDataRepository extends JpaRepository<FileData, Long> {
Optional<FileData> findByName(String fileName);
}
결과
POST - filePath에 파일이 저장(업로드)
GET - GET은 위와 같음.(다운로드)
파일 보여주기 api
@GetMapping("/view/{fileName}")
public ResponseEntity<Resource> viewFileGET(@PathVariable String fileName){
return fileUtil.getFile(fileName);
}
fileUtil 중 파일 가져오기 메서드 getFile
/**
* 파일 가져오기
*
* @param fileName 가져올 파일명
* @return 파일 리소스
*/
public ResponseEntity<Resource> getFile(String fileName) {
Resource resource = new FileSystemResource(uploadPath + File.separator + fileName);
if (!resource.exists()) {
resource = new FileSystemResource(uploadPath + File.separator + "default.jpeg");
}
HttpHeaders headers = new HttpHeaders();
try {
headers.add("Content-Type", Files.probeContentType(resource.getFile().toPath())); // Content-Type
image/jpeg
} catch (Exception e) {
return ResponseEntity.internalServerError().build();
}
return ResponseEntity.ok().headers(headers).body(resource);
}
결과