Image URL → MultipartFile 변환하기

qpwoeiru·2024년 4월 21일
0

계속 진행하던 팀 프로젝트에서 DB 값들을 좀 둘러보다가 일정에 들어 있는 국가 대표 이미지 링크를 열어봤다. 근데 내가 이미지 url 만료 기간이 있는지 모르고 DB에 값을 넣었었다... 우리 서비스의 프로필 사진을 azure storage에 저장하는데, 국가 대표 이미지도 MultipartFile로 변환해서 storage에 저장하기로 했다.

프로필 사진을 저장하는데 필요했던 ImageService의 코드를 사용하기로 했다. 이미지 파일은 MultipartFile로 입력 받기 때문에 기존 upload 메서드를 사용하고자 Image url을 MultipartFile로 변환하기로 했다.

먼저 MultipartFile로 변환 될 수 있는 형태가 File, byte[]가 있어서 두 가지 변환 과정을 생각했다.

  1. image urlFileMultipartFile
  2. image urlbyte[]MultipartFile

1. Image url → File → MultipartFile

결과적으로는.. 실패한 방법이다. image url → File 변환은 가능하지만, File → MultipartFile 과정에서 사용되는 CommonsMultipartFile가 2023년에 deprecated 됐다고 한다.
Upgrading to Spring Framework 6.x안돼서 한참 찾았는데 CommonsMultipartResolver가 drop되면서 그와 관련된 CommonsMultipartFile도 같이 drop됐다. StandardServletMultipartResolver 사용을 권장 한다는데.. 찾아봐도 File → MultipartFile 변환은 CommonsMultipartFile을 사용하는 방법들이 많고, 최근에 변경된 사항이라 그런지 File → MultipartFile 변환 하는 자료를 찾기가 너무 어려워서 일단 보류했다.


2. Image url → byte[] → MultipartFile

Image url -> byte[]

image url을 byte 배열로 변환하기 위해선 URL의 입력 스트림을 열어 byte 배열로 이미지 데이터를 읽어오고, 출력 스트림에 이를 jpg 확장자로 쓰는 과정을 거친다.

InputStream
Java의 byte 기반 입력 소스를 추상화한 클래스로, 파일, 메모리 버퍼 등 다양한 입력으로부터 바이트 데이터를 읽어올 때 사용된다.

ByteArrayOutputStream
메모리 버퍼에 byte 배열을 저장하는 클래스이다. toByteArray() 메서드를 호출해 버퍼에 저장된 byte 배열을 반환할 수 있다.

BufferedImage
메모리에 로드 된 이미지 데이터를 나타내는 클래스이다.

ImageIO
이미지 파일을 읽고 쓰는 작업을 수행하는 클래스이다. read() 메서드로 InputStream, 파일 등으로부터 이미지 데이터를 읽어올 수 있고, write() 메서드로는 BufferedImage, OutputStream으로 출력할 수 있다.

URL url = new URL(imageUrl);

// image url의 input stream, byte 배열로 저장할 output stream 열기
try(InputStream inputStream = url.openStream();
	ByteArrayOutputStream bos = new ByteArrayOutputStream()) {
    	// ImageIO.read()로 image url의 이미지 데이터 읽어오기
		BufferedImage urlImage = ImageIO.read(inputStream);
		// 메모리에 로드 된 이미지 데이터를 output stream에 jpg 확장자로 저장
        ImageIO.write(urlImage, "jpg", bos);
        // byte 배열로 변환
        byte[] byteArray = bos.toByteArray(); 
}

InputStream과 OutputStream은 메모리를 사용하므로 다 사용하면 close가 필요하다. 따라서 InputStream, ByteArrayOutputStream은 try-with-resources문의 resource로 열어, 동작이 종료되면 자동으로 close 되도록 한다.

byte[] -> MultipartFile

byte 배열을 MultipartFile 객체로 변환하기 위해서는 MultipartFile 인터페이스를 구현한 CustomMultipartFile 클래스가 필요하다.

CustomMultipartFile 클래스를 생성해 변환하고자 하는 데이터의 byte 배열을 input 필드로 추가한다. String filename로 이미지 파일 이름도 추가하고 구현을 위한 메서드들을 오버라이딩 한다.

public class CustomMultipartFile implements MultipartFile {
	private byte[] input;
	private String filename;

	public CustomMultipartFile(byte[] input,String filename) {
		this.input = input;
		this.filename = filename;
	}

	@Override
	public String getName() {
		return null;
	}

	@Override
	public String getOriginalFilename() {
		return filename;
	}

	@Override
	public String getContentType() {
		return null;
	}

	@Override
	public boolean isEmpty() {
		return input == null || input.length == 0;
	}

	@Override
	public long getSize() {
		return input.length;
	}

	@Override
	public byte[] getBytes() throws IOException {
		return input;
	}

	@Override
	public InputStream getInputStream() throws IOException {
		return new ByteArrayInputStream(input);
	}

	@Override
	public void transferTo(File dest) throws IOException, IllegalStateException {
		try(FileOutputStream fos = new FileOutputStream(dest)){
			fos.write(input);
		}
	}
}

transferTo(File dest)는 파일의 내용을 지정된 dest 파일에 쓰는 역할이다. byte 배열인 input을 dest 파일에 쓰는 작업을 한다.

이미지 데이터의 byte 배열과 저장할 이미지의 이름을 넘겨 CustomMultipartFile 객체를 생성하면 MultipartFile 변환 끝!

MultipartFile multipartFile = new CustomMultipartFile(byteArray, imageUrl);

Image url → byte[] → MultipartFile 전체 코드

private String convertUrlToMultipartFile(String imageUrl) throws IOException {
		URL url = new URL(imageUrl);
		
		try(InputStream inputStream = url.openStream();
			ByteArrayOutputStream bos = new ByteArrayOutputStream()) {
			// 1) image url -> byte[] 
			BufferedImage urlImage = ImageIO.read(inputStream); 
			ImageIO.write(urlImage, "jpg", bos);
			byte[] byteArray = bos.toByteArray();
			// 2) byte[] -> MultipartFile 
			MultipartFile multipartFile = new CustomMultipartFile(byteArray, imageUrl);
			return imageService.uploadImageAndGetName(multipartFile); // image를 storage에 저장하는 메서드 호출
		}
	}

참고
[JAVA] 이미지 url을 byte array로 변환
Convert byte[] to MultipartFile in Java | Baeldung

0개의 댓글

관련 채용 정보