국비지원

[JAVA] BLOB, Path/FIles, encode와 decode, properties 파일 활용

yeon-96 2023. 1. 13. 23:19
반응형

BLOB이란

Binary Large Object, BLOB은 데이터베이스 관리 시스템의 하나의 엔티티로서 저장되는 이진 데이터의 모음이다.

이진데이터 형태로 저장되는 큰 객체를 다룰 때 쓰는 단위, 객체라고 보면된다.

 BLOB 객체와 IO의 관계

mysql에도 BLOB 자료형의 컬럼을 생성할 수 있으며, java와 IO 상호작용이 가능하다.

 

package test;

import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.nio.file.StandardCopyOption;
import java.sql.Blob;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;

import dbutil.ConnectionProvider;

public class Main {
	private static void copy(InputStream inputStream, String filename) {
		try (BufferedInputStream bis = new BufferedInputStream(inputStream);
				BufferedOutputStream bos = new BufferedOutputStream(
						new FileOutputStream(filename))) {
			int b = 0;
			while ((b = bis.read()) != -1) {
				bos.write(b);
			}
			
		} catch (FileNotFoundException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}
	}
	
	public static void main(String[] args) {
		try (Connection conn = ConnectionProvider.makeConnection();
				PreparedStatement stmt = conn.prepareStatement("SELECT * FROM image WHERE id = ?")) {
					stmt.setInt(1, 1);
					
					try (ResultSet rs = stmt.executeQuery()) {
						if (rs.next()) {
							int id = rs.getInt("id");
							String filename = rs.getString("filename");
							Blob image = rs.getBlob("image");
							InputStream inputStream = image.getBinaryStream();
							// 복사 시 옵션, StandardCopyOption.REPLACE_EXISTING = 이미 있는거 덮어쓸거임
							Files.copy(inputStream, Paths.get(filename), StandardCopyOption.REPLACE_EXISTING);
						}
					} catch (IOException e) {
						e.printStackTrace();
					}
					System.out.println("파일 복사 완료");
				} catch (SQLException e) {
					e.printStackTrace();
				} 
	}
}

이렇게 자유롭게 이미지를 바이너리형태로 읽어와 프로젝트파일에 복사하는 것이 가능하다.

 

필요한 이미지 리소스를 클래스로드와 동시에 MAP에 저장하는 형태로 응용해보는 기회는 가져보자.


 

Path클래스 

기존 파일 불러오는 법은 File 클래스를 활용해 파일의 경로를 확인받고 파일을 사용할 수 있었다.

JAVA 1.7버전 이후로 nio 패키지에 Files, Path, Paths 등의 클래스를 활용해 더 편리, 최신의 방법으로 접근 가능하다.

public class Main2 {
	public static void main(String[] args) {
		// 기존 파일 불러오는 법
		// File file = new File("파일경로");
		// 자바 1.7버전 이후부터 가능한 더 최신(대응되는) 방법
		Path path = Paths.get("D:\\kimkyeongyeon\\춘식\\춘식6.png"); //경로를 나타내는 다른방법 : "D:\\kimkyeongyeon\\춘식", "춘식6.png"
		try {
			Files.copy(path, Paths.get("D:\\myfolder\\복사본.png"));
			System.out.println("파일 복사 완료");
		} catch (IOException e) {
			e.printStackTrace();
		}
	}
}

Path 인터페이스로 객체를 읽어온 뒤 Files 클래스의 copy 메소드를 활용해 복사본을 생성할 수 있다.

1. Path야 해당경로에 춘식6.png 읽어와줘 ~

2. Files 야 금방 읽은 경로랑 같은 경로에 춘식6.png파일 복사본.png이름으로 copy해 줘 ~

 

 


 

Files 의 다양한 메소드 살펴보기

import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.Arrays;
import java.util.List;

public class Main3 {
	public static void main(String[] args) throws IOException {
		List<String> fileContent
		= Files.readAllLines(Paths.get("텍스트파일경로")); // 모든 텍스트를 읽어, 각 행을 List의 원소로 반환
		
//		Files.readAllBytes(path) byte[]로 파일 내용 모두 읽어옴. 
		
//		Files.write(Paths.get("출력경로"), fileContent); // list의 String을 각 행으로 출력해줌.
		Files.write(Paths.get(".\\텍스트출력.txt"), Arrays.asList("문자열 1행", "문자열 2행", "문자열 3행"));
//		Files.write(경로, 바이트배열); 해당 경로에 byte[] 모든 내용 출력
		
	
	}
}

Files.readAllLines 모든 텍스트를 읽어, 각 행을 리스트 인덱스 원소로 반환~

각 행이 들어감 ~
출력 !

Files.write는 LIst의  String을 각 행으로 출력해줌

리스트 값이 파일로 출력됩니다 !


 

encode와 decode

2진데이터를 사람이 읽을 수 있는 문자형태로 변환하는 작업을 encode,

문자형태의 데이터를 2진데이터로 변환하는 것을 decode라 한다.

JAVA util 패키지의 Base64 클래스를 활용하면 encode, decode를 쉽게 할 수 있다.

package test;
import java.util.Arrays;
import java.util.Base64;

public class Main4 {
	public static void main(String[] args) {
		// 베이스64 encoding
		// 2진데이터를 영문자로 변경 = encode, 영문자에서 2진데이터로 변경 = decode
		String line = "문자열원본";
		
		// 파일의 2진데이터형으로 표현
		System.out.println(Arrays.toString(line.getBytes()));
		
		// 인코드 작업
		String encoded = Base64.getEncoder().encodeToString(line.getBytes());
		System.out.println(encoded);
		
		// 디코드 작업
		byte[] decode = Base64.getDecoder().decode(encoded);
		System.out.println("디코딩된 원본 : " + new String(decode));
		
	}
}

우리가 읽을 수 있는 "문자열원본"이라는 문장은 

2진 데이터로 표현하면 ..

[-21, -84, -72, -20, -98, -112, -20, -105, -76, -20, -101, -112, -21, -77, -72]

2진 데이터가 인코드 작업을 거친 후..

66y47J6Q7Je07JuQ67O4

다시 decode 작업을 거친 다면..

[-21, -84, -72, -20, -98, -112, -20, -105, -76, -20, -101, -112, -21, -77, -72]

이 형태를 String으로 변환하게 되면..

"문자열원본"을 받아 볼 수 있다.

 

다음의 코드는 Image를 Base64를 활용해 encode하여 DB로 전송하는 방법이다.


public class Main5 {
	private static void isertBase64Image(String encode) {
		String sql = "INSERT INTO image (filename, image) VALUES (?, ?)";
		try (Connection conn = ConnectionProvider.makeConnection();
				PreparedStatement stmt = conn.prepareStatement(sql)) {
			stmt.setString(1, "춘식6.png");
			stmt.setString(2, encode);
			
			stmt.executeUpdate();
		} catch (SQLException e) {
			e.printStackTrace();
		}
	}
	
	public static void main(String[] args) {
		Path image = Paths.get("D:\\kimkyeongyeon\\workspace\\230102-01\\춘식6.png");
		
		// 2진자료를 모든 바이트를 읽어서 ~ 배열로 ~
		byte[] bytes;
		try {
			bytes = Files.readAllBytes(image);
			String encode = Base64.getEncoder().encodeToString(bytes);
			
			isertBase64Image(encode);
			System.out.println("base64 인코딩된 이미지 행 추가 완료");
		} catch (IOException e) {
			e.printStackTrace();
		}
	}
}

2진자료를 모두 읽어 byte배열로 저장 ! 

그리고 sql쿼리로 DB로 전송 ~

 

마찬가지로 DB의 image를 decode하여 읽어낼 수 있음~!

public class Main6 {
	public static byte[] decodeBase64(String encode) {
		return Base64.getDecoder().decode(encode);
	}
	
	public static void selectEncodedImage(int id) {
		String sql = "SELECT * FROM image WHERE id = ?";
		try (Connection conn = ConnectionProvider.makeConnection();
				PreparedStatement stmt = conn.prepareStatement(sql)) {
			stmt.setInt(1, id);
			
			try (ResultSet rs = stmt.executeQuery()) {
				if (rs.next()) {
					String encode = rs.getString("image");
					byte[] decode = decodeBase64(encode);
					Files.write(Paths.get(".\\디코딩결과.png"), decode);
				}
			} catch (IOException e) {
				e.printStackTrace();
			}
			
		} catch (SQLException e) {
			e.printStackTrace();
		}
		
	}
	
	public static void main(String[] args) {
		selectEncodedImage(2);
	}
}

Connection 사용 시 properties 파일 활용 법

다음 코드는 properties파일을 활용해 파일만 수정하면 DB 연결을 편하게 할 수 있는 방법이다.

public static Connection makeConnection() throws SQLException {
		Connection conn = null;
		// 주소에 데이터베이스 USE 사용가능
		conn = DriverManager.getConnection(PROPERTIES.getProperty("url"), PROPERTIES.getProperty("username"), PROPERTIES.getProperty("password"));
		return conn;
	}

코드는 이러하고, properties의 형식은 다음과 같다. 

 

우선 Properties 객체를

private static final Properties PROPERTIES = new Properties();

이렇게 생성하고, Connection은 DriverManager의 getConnection메소드를 활용한다.

파라미터로(PROPERTIES.getproperty("")의 파라미터로 원하는 값인 url, username, password를 문자열로 넣어주면

properties 파일에서 해당 문자열을 찾아 값을 넣어주고 connection을 생성해준다 ~!

반응형