본문으로 건너뛰기

파일 조작

자바에서 파일을 다루기 위해서는 File 클래스를 사용한다.
파일 뿐만이 아니라 디렉토리도 나타낸다.
윈도우의 경우에는 \ 로 디렉토리를 구별하는데

\  -> 윈도우에서는 이스케이프 하기위해  \\ 

이스케이프 하기위해 역슬래시를 두개를 쓴다.

자바에서 디렉토리를 삭제할때는 디렉토리가 비어있어야 한다.

실행 유저의 권한에 의해서 삭제가 실패하거나 한다면 예외가 발생한다

NIO라고 자바7에 추가된것이 있는데 차이에 대해서는 따로 포스팅 하고
이 페이지에서는 예제만 다뤄보자


파일 디렉토리의 삭제(FIle클래스)

public class ExerciseFile {

public static void main(String[] args) {

// 파일 혹은 디렉토리를 참조변수화 한다.
File file = new File("test-file.txt");

if (file.delete()) {
// 정상적으로 삭제 됐을 시
System.out.println("삭제성공");
} else {
// 삭제가 안됐을 때
System.out.println("삭제실패");
}
}
}

기본적으로 위에같이 지정해주면 프로젝트의 루트 디렉토리 기준으로 파일명이 지정된다.
프로젝트 루트 디렉토리에 test-file.txt를 생성하고 실행하면

삭제성공

해당 파일이 없으면

삭제실패

라고 나온다.


파일 디렉토리의 삭제(NIO.2)

public class ExerciseFile {

public static void main(String[] args) throws IOException {

// 파일 혹은 디렉토리를 참조변수화 한다.
Path path1 = Paths.get("test-file.txt");

// 파일 혹은 디렉토리를 삭제한다.
Files.delete(path1);

////////////////////////////////////
// 조건 식으로 삭제할 경
////////////////////////////////////

Path path2 = Paths.get("test-file2우.txt");

if (Files.deleteIfExists(path2)) {
// 정상적으로 삭제 됐을 시
System.out.println("삭제성공");
} else {

// 삭제가 안됐을 때(파일이나 디렉토리가 없을 때)
System.out.println();
}
}
}

파일이나 디렉토리가 존재하지 않으면 익셉션

Exception in thread "main" java.nio.file.NoSuchFileException: test-file.txt
at java.base/sun.nio.fs.UnixException.translateToIOException(UnixException.java:92)
at java.base/sun.nio.fs.UnixException.rethrowAsIOException(UnixException.java:106)
at java.base/sun.nio.fs.UnixException.rethrowAsIOException(UnixException.java:111)
at java.base/sun.nio.fs.UnixFileSystemProvider.implDelete(UnixFileSystemProvider.java:249)
at java.base/sun.nio.fs.AbstractFileSystemProvider.delete(AbstractFileSystemProvider.java:105)
at java.base/java.nio.file.Files.delete(Files.java:1152)
at org.example.ExerciseFile.main(ExerciseFile.java:17)

파일이나 디렉토리가 존재하면 정상적으로 파일이 삭제된 것을 확인 할 수 있다.

[localhost practice]$ touch test-file.txt
[localhost practice]$ ls
pom.xml src target test-file.txt

# 실행 후
[localhost practice]$ ls
pom.xml src target
[localhost practice]$

파일 디렉토리의 이름 변경

public class ExerciseFile {

public static void main(String[] args) {

// 변경전 파일
File oldFile = new File("oldFile.txt");

// 변경 후 파일
File newFile = new File("newFile.txt");

if (oldFile.renameTo(newFile)) {
System.out.println("파일이름 변경 성공");
} else {
System.out.println("파일이름 변경 실패");
}
}
}
oldFile.txt  pom.xml  src  target
# 변경후
newFile.txt pom.xml src target

Unit계열에서는 변경 이름이 이미 존재한다면 덮어쓰기가 되지만 Windows계열에서는 변경되지 않고 false를 반환한다
또한, 엑세스 권한에 의해서 변경이 불가능 할 경우에는 예외가 발생된다

파일 디렉토리의 이름 변경(NIO.2)

public class ExerciseFile {

public static void main(String[] args) throws IOException {

// 변경전 파일
Path oldPath = FileSystems.getDefault().getPath(
"oldFile.txt"
);

// 변경후
Path newPath = Files.move(
oldPath, oldPath.resolveSibling("newFile.txt")
);

// 변경후를 출력
System.out.println(newPath.toString());
}
}
newFile.txt

파일이 없으면 IO익셉션이 발생한다.

xception in thread "main" java.nio.file.NoSuchFileException: oldFile.txt
at java.base/sun.nio.fs.UnixException.translateToIOException(UnixException.java:92)
at java.base/sun.nio.fs.UnixException.rethrowAsIOException(UnixException.java:106)
at java.base/sun.nio.fs.UnixException.rethrowAsIOException(UnixException.java:111)
at java.base/sun.nio.fs.UnixCopyFile.move(UnixCopyFile.java:429)
at java.base/sun.nio.fs.UnixFileSystemProvider.move(UnixFileSystemProvider.java:267)
at java.base/java.nio.file.Files.move(Files.java:1432)
at org.example.ExerciseFile.main(ExerciseFile.java:20)

파일 복사

public class ExerciseFile {

public static void main(String[] args) throws IOException {

// 1. 원본 File, 복사할 File 준비
File file = new File("file1.txt");
File newFile = new File("file1_bak.txt");

// 2. FileInputStream, FileOutputStream 준비
FileInputStream input = new FileInputStream(file);
FileOutputStream output = new FileOutputStream(newFile);

// 3. 한번에 read하고, write할 사이즈 지정
byte[] buf = new byte[1024];

// 4. buf 사이즈만큼 input에서 데이터를 읽어서, output에 쓴다.
int readData;
while ((readData = input.read(buf)) > 0) {
output.write(buf, 0, readData);
}

// 5. Stream close
input.close();
output.close();
}
}

readData = input.read(buf);
buf(버퍼)에 데이터를 읽어서 넣고, buf(버퍼)로 읽은 총 byte 수를 리턴
파일 끝에 도달하여, 더 이상 읽을 데이터가 없을 경우 -1을 리턴

output.write(buf, 0, readData);
buf(버퍼)에 담긴 데이터를,
0번째 offset부터,
readData 길이만큼,
output stream에 씀

파일 복사(NIO.2)

public class ExerciseFile {

public static void main(String[] args) throws IOException {

// 원본 File, 복사할 File 준비
Path srcPath = Paths.get("file1.txt");
Path targetPath = Paths.get("file1_bak.txt");

// 복사할 File이 이미 있는지 확인
System.out.println(Files.exists(targetPath));

// 덮어쓰기 옵션으로 지정해서 복사
Files.copy(srcPath, targetPath, StandardCopyOption.REPLACE_EXISTING);
}
}
  • StandardCopyOption.REPLACE_EXISTING : 덮어쓰기옵션 복사
  • StandardCopyOption.COPY_ATTRIBUTES : 파일속성을 포함하여 복사
  • StandardCopyOption.NOFOLLOW_LINKS : 파일이 symbolic link이면, 링크 대상이 아닌, symbolic link 자체가 복사

파일 복사(채널 스타일)

FileChannel 클래스는 파일의 읽기, 쓰기, 맵핑 등을 위한 채널을 제공한다.
이 클래스를 사용해서 한 파일에서 다른 파일로 컨텐츠를 전송할 수 있다.

transferFrom(), transferTo() 메소드를 이용해서 내용을 복사할 수 있다.

public abstract long transferFrom(ReadableByteChannel src, long position, long count)

파라미터로 전달된
src 파일채널로부터, position 위치부터, count 사이즈만큼 데이터를 복사한다.

public abstract long transferTo(long position, long count, WritableByteChannel target)

파라미터로 전달된
target 파일 채널로, position 위치부터, count 사이즈만큼 데이터를 복사한다.


public class ExerciseFile {

public static void main(String[] args) throws IOException {

// 1. 원본 File, 복사할 File 준비
RandomAccessFile file = new RandomAccessFile("file1.txt", "r");
RandomAccessFile newFile = new RandomAccessFile("file1_bak.txt", "rw");

// 2. FileChannel 생성
FileChannel source = file.getChannel();
FileChannel target = newFile.getChannel();

// 3. 복사
source.transferTo(0, source.size(), target);

// 또는
//target.transferFrom(source, 0, source.size());

}
}

transferTo() 또는 transferFrom() 메소드를 이용하여 데이터를 복사할 수 있다.
이 메소드는 데이터를 count(source.size())만큼 target에 써주기 때문에,
만약, target 파일이 이미 존재하고, target 파일의 데이터가 더 길면,
나머지 부분은 그대로 남아있게 된다.

예를 들어, 아래와 같은 파일을 위 코드를 사용해서 source에서 target으로 복사한다고 해보자.

  • source 파일 : "abcd"
  • target 파일 : "efghi"
  • 그러면 target 파일은 "abcdi"가 된다

source 파일 길이의 byte만큼 target 파일에 써주었기 때문

파일 이동

before / after 디렉토리를 각각 만들고 before 에는 oldFile.txt을 만들어 놓는다.
실행 후 확인해보면 before의 oldFile.txt 파일이 after쪽으로 옮겨진 것을 알 수 있다.

public class ExerciseFile {

public static void main(String[] args) throws IOException {

// 이동 전 파일
File oldFile = new File("before", "oldFile.txt");

// 이동 후 파일
File newFile = new File("after", "newFile.txt");

if(oldFile.renameTo(newFile )) {
System.out.println("이동성공");
} else {
System.out.println("이동실패");
}

}
}

쓰기 권한이 없을때에는 예외가 발생하며, 이미 존재하는 파일에 덮어쓰기를 할 경우 unix 계열에서는 true이지만 윈도우 계열에서는 false이다.

파일 이동(NIO.2)

public class ExerciseFile {

public static void main(String[] args) throws IOException {

// 이동 전
File oldFile = new File("before", "oldFile.txt");
Path oldPath = Paths.get(oldFile.toString());

// 이동 후
File newFile = new File("after", "newFile.txt");
Path newPath = Paths.get(newFile.toString());

// 이동 전 확인
System.out.println("이동전 확인 : " + Files.exists(newPath));

Files.move(oldPath, newPath, StandardCopyOption.REPLACE_EXISTING);

System.out.println("이동후 확인 : " + Files.exists(newPath));
}
}

파일의 사이즈를 확인

public class ExerciseFile {

public static void main(String[] args) throws IOException {

File file = new File("test-file.txt");

System.out.println("파일 사이즈 : " + file.length());
}
}
파일 사이즈 : 34

파일의 사이즈를 바이트 단위로 표시해준다

파일의 사이즈를 확인(NIO.2)

public class ExerciseFile {

public static void main(String[] args) throws IOException {

Path path = Paths.get("test-file.txt");

System.out.println("파일 사이즈 : " + Files.size(path));
}
}
파일 사이즈 : 34

마찬가지로 파일의 사이즈를 바이트 단위로 표시해준다 메서드 명이 File.length() 보다 Files.size() 가 더 직관적이다

파일의 최종 갱신일의 취득

public class ExerciseFile {

public static void main(String[] args) throws IOException {

File file = new File("test-file.txt");

Date date = new Date(file.lastModified());

System.out.println("최종 갱신일 : " + date);
}
}
최종 갱신일 : Tue Jan 18 02:01:16 JST 2022

파일의 최종 갱신일의 취득(NIO.2)

public class ExerciseFile {

public static void main(String[] args) throws IOException {

Path path = Paths.get("test-file.txt");

FileTime ft = Files.getLastModifiedTime(path);

System.out.println("최종 갱신일 : " + ft);
}
}
최종 갱신일 : 2022-01-17T17:01:16.739571489Z

엇? 표시 형식이 다르게 출력되네??

파일 갱신일을 변경

public class ExerciseFile {

public static void main(String[] args) throws IOException {

// 파일 이름 취득
File file = new File("test-file.txt");

// 변경전 시각 취득
Date date1 = new Date(file.lastModified());

// 현재 갱신 시각 출력
System.out.println("파일의 현재 갱신일 : " + date1);

// 2022년 1월 1일 11시 22분 33초로 달력 인스턴스 작성
Calendar cal = Calendar.getInstance();
cal.set(2022, 0, 2, 11, 22, 33);

// 최종 갱신 시각을 변경
file.setLastModified(cal.getTimeInMillis());

// 변경 후의 갱신 시각을 취득
Date date2 = new Date(file.lastModified());

// 변경 후의 갱신 시각을 출력
System.out.println("변경 후의 갱신일 : " + date2);
}
}
파일의 현재 갱신일 : Tue Jan 18 02:01:16 JST 2022
변경 후의 갱신일 : Sun Jan 02 11:22:33 JST 2022

ls -l 로 확인 해 보면 변경 된 것을 알 수 있다.

파일 갱신일을 변경(NIO.2)

public class ExerciseFile {

public static void main(String[] args) throws IOException {

Path path = Paths.get("test-file.txt");

FileTime ft1 = Files.getLastModifiedTime(path);

System.out.println("변경 전 갱신시각 출력 : " + ft1);

Calendar cal = Calendar.getInstance();
cal.set(2022, 0, 2, 10, 20, 30);

// 갱신시각을 변경
Files.setLastModifiedTime(path, FileTime.fromMillis(cal.getTimeInMillis()));

// 변경후의 갱신시각을 취득
FileTime ft2 = Files.getLastModifiedTime(path);

System.out.println("변경 후 갱신시각 출력 : " + ft2);
}
}
변경 전 갱신시각 출력 : 2022-01-02T02:22:33.402Z
변경 후 갱신시각 출력 : 2022-01-02T01:20:30.272Z

패스에서 파일명을 취득

public class ExerciseFile {

public static void main(String[] args) throws IOException {

File file = new File("temp", "test-file.txt");

System.out.println("path : " + file.toString());

String fileName = file.getName();

System.out.println("file name : " + fileName);
}
}
path : temp/test-file.txt
file name : test-file.txt

파일이나 패스가 실제로 존재하지 않더라도 출력이 된다. file.getName() 메서드로 풀패스명+파일명 에서 파일명만 짤라준다

패스에서 파일명을 취득(NIO.2)

public class ExerciseFile {

public static void main(String[] args) throws IOException {

// 대상 패스를 설정
Path path1 = FileSystems.getDefault().getPath("temp/targetFile.txt");
// 대상 패스 출력
System.out.println("패스 : " + path1);

// 파일명 취득
Path path2 = path1.getFileName();
// 파일명 출력
System.out.println("파일명 : " + path2);

// getNameCount 로 파일명 취득 후 출력
System.out.println(
"파일명 : " + path1.getName(path1.getNameCount() - 1)
);

}
}
패스 : temp/targetFile.txt
파일명 : targetFile.txt
파일명 : targetFile.txt

파일 / 디렉토리의 엑세스 권한을 확인

public class ExerciseFile {

public static void main(String[] args) throws IOException {

File file = new File("test-file.txt");

// 실행권한 출력
System.out.println("실행 권한 : " + file.canExecute());

// 읽기 권한 출력
System.out.println("읽기 권한 : " + file.canRead());

// 쓰기 권한 출력
System.out.println("쓰기 권한 : " + file.canWrite());
}
}
실행 권한 : false
읽기 권한 : true
쓰기 권한 : true

파일 / 디렉토리의 엑세스 권한을 확인(NIO.2)

public class ExerciseFile {

public static void main(String[] args) {

Path path = Paths.get("test-file.txt");

// 실행권한 출력
System.out.println("실행 권한 : " + Files.isExecutable(path));

// 읽기 권한 출력
System.out.println("읽기 권한 : " + Files.isReadable(path));

// 쓰기 권한 출력
System.out.println("쓰기 권한 : " + Files.isWritable(path));
}
}
실행 권한 : false
읽기 권한 : true
쓰기 권한 : true

파일명에서 확장자를 취득

public class ExerciseFile {

public static void main(String[] args) {

File[] files = new File("./").listFiles();

for (File file : files) {

// 대상 파일이 없으면 처리하지 않는다
if (!file.isFile()) {
continue;
}

String fileName = file.getName();

int idx = fileName.lastIndexOf(".");
String extName = (-1 != idx)
? fileName.substring(idx + 1)
: "확장자없음";

System.out.println(
"파일명 : " + fileName + "\t확장자명 :" + extName
);
}
}
}
파일명 : pom.xml	확장자명 :xml
파일명 : test-file.txt 확장자명 :txt

확장자를 취득하려면
먼저 파일명을 취득하고 그 파일명에 대해서 String.lastIndexOf 메서드를 사용해서 확장자의 구분문자인 . 을 취득
그후 String.substring 메서드의 파라미터를 확장자의 구분문자의 위치 +1 로 지정하여, 확장자의 구분문자 이후의 문자열을 취득하는 방법 으로 한다

패스나 구분문자열 상관없이 디렉토리명과 파일명을 결합

public class ExerciseFile {

public static void main(String[] args) {

File parent = new File("parent1");
// parent를 참조
File file1 = new File(parent, "targetFile.txt");

// parent1 을 문자열로 지정(파라미터로 넘김)
File file2 = new File("parent1", "targetFile.txt");

// parent2 를 문자열로 지정(파라미터로 넘김)
File file3 = new File("parent2", "targetFile.txt");

System.out.println(file1);
System.out.println(file2);
System.out.println(file3);
}
}
parent1/targetFile.txt
parent1/targetFile.txt
parent2/targetFile.txt

parent1/targetFile.txt parent1/targetFile.txt parent2/targetFile.txt

패스나 구분문자열 상관없이 디렉토리명과 파일명을 결합(NIO.2)

public class ExerciseFile {

public static void main(String[] args) {


Path path1 = Paths.get("parent1", "targetFile.txt");
Path path2 = Paths.get("parent2", "parent1", "targetFile.txt");

System.out.println(path1.toString());
System.out.println(path2.toString());
}
}
parent1/targetFile.txt
parent2/parent1/targetFile.txt

오옹??

지정한 패스가 디렉토리인지 파일인지 확인

public class ExerciseFile {

public static void main(String[] args) {

File file1 = new File("./");
File file2 = new File("./test-file.txt");

System.out.println("파일1 패스가 디렉토리임? : " + file1.isDirectory());
System.out.println("파일2 패스가 디렉토리임? : " + file2.isDirectory());

System.out.println("파일1 패스가 파일임? : " + file1.isFile());
System.out.println("파일2 패스가 파일임? : " + file2.isFile());
}
}
파일1 패스가 디렉토리임? : true
파일2 패스가 디렉토리임? : false
파일1 패스가 파일임? : false
파일2 패스가 파일임? : true
``

### 지정한 패스가 디렉토리인지 파일인지 확인(NIO.2)

```java
public class ExerciseFile {

public static void main(String[] args) {

Path path1 = Paths.get("./");
Path path2 = Paths.get("./test-file.txt");

System.out.println("path1은 디렉토리임? : " + Files.isDirectory(path1));
System.out.println("path2은 디렉토리임? : " + Files.isDirectory(path2));

System.out.println("path1은 파일임? : " + Files.isRegularFile(path1));
System.out.println("path2은 파일임? : " + Files.isRegularFile(path2));

}
}
path1은 디렉토리임? : true
path2은 디렉토리임? : false
path1은 파일임? : false
path2은 파일임? : true

지정한 패스가 존재 하는지 확인

public class ExerciseFile {

public static void main(String[] args) {

File file = new File("./");

System.out.println("file 이 존재? : " + file.exists());

}
}
file 이 존재? : true

지정한 패스가 존재 하는지 확인(NIO.2)

public class ExerciseFile {

public static void main(String[] args) {

Path path = Paths.get("./");

System.out.println("file 이 존재? : " + Files.exists(path));

}
}
file 이 존재? : true

디렉토리를 생성

public class ExerciseFile {

public static void main(String[] args) {

File file1 = new File("parent1");
File file2 = new File("parent2", "child");

System.out.println("단일 디렉토리 작성 결과 : " + file1.mkdir());
System.out.println("복수 디렉토리 작성 결과 : " + file2.mkdirs());
}
}
단일 디렉토리 작성 결과 : true
복수 디렉토리 작성 결과 : true

단일 디렉토리 작성일때는 mkdir() 복수디렉토리 작성 일때는(하위디렉토리까지) mkdirs() 를 쓴다

디렉토리를 생성(NIO.2)

public class ExerciseFile {

public static void main(String[] args) throws IOException {

Path path1 = Paths.get("parent1");
Path path2 = Paths.get("parent2", "child");

System.out.println("단일 디렉토리 작성 결과 : " + Files.createDirectory(path1));
System.out.println("복수 디렉토리 작성 결과 : " + Files.createDirectories(path2));
}
}
단일 디렉토리 작성 결과 : parent1
복수 디렉토리 작성 결과 : /home/user1/바탕화면/Maven-Quick-Start/practice/parent2/child

단일 디렉토리 작성일때는 createDirectory() 복수디렉토리 작성 일때는(하위디렉토리까지) createDirectories() 를 쓴다

디렉토리 안의 파일 일람을 출력

public class ExerciseFile {

public static void main(String[] args) throws IOException {

File file = new File("./");

// list메서드를 통한 출력
String[] fileList1 = file.list();

for (String fileName : fileList1) {
System.out.println(fileName);
}
System.out.println();

// listFiles메서드를 통한 출력
File[] fileList2 = file.listFiles();

for (File fileList : fileList2) {
System.out.println(fileList);
}
}
}
.idea
pom.xml
src
target
test-file.txt

./.idea
./pom.xml
./src
./target
./test-file.txt

File.list() 메서드 파일명만(리눅스에서는 디렉토리도 같게 취급)
File.listFiles() 메서드는 경로도 포함한다.
list 는 스트링의 배열 listFiles 는 파일의 배열이군..

디렉토리 안의 파일 일람을 출력(NIO.2)

public class ExerciseFile {

public static void main(String[] args) throws IOException {

Path path = Paths.get("./");

try (DirectoryStream<Path> stream = Files.newDirectoryStream(path)) {
for (Path file : stream) {
System.out.println(file.getFileName());
}
}
}
}
.idea
pom.xml
src
target
test-file.txt

DirectoryStream 인터페이스와 스트림 이므로 마지막에 Close 할 필요가 있다. Closeable 인터페이스를 상속받고 있으므로 try-with-resources 문을 사용하는 것도 가능하다

파일 일람에 필터를 적용

public class ExerciseFile {

public static void main(String[] args) throws IOException {

File targetFile = new File("./");

File[] fileList1 = targetFile.listFiles(new ExerciseFilter1());

System.out.println("FileNameFilter로 필터링");
for (File file : fileList1) {
System.out.println(file);
}
}
}
public class ExerciseFilter1 implements FilenameFilter {

@Override
public boolean accept(File dir, String name) {
return name.startsWith("file");
}
}
FileNameFilter로 필터링
./file1.txt
./file2.txt

필터링은 좀.. 실사용 할 때 자료를 더 찾아보는 쪽으로...

상대적 패스와 절대 패스

public class ExerciseFile {

public static void main(String[] args) {

File file = new File("test-file.txt");

// 상대 패스
System.out.println(file);

// 절대 패스
System.out.println(file.getAbsolutePath());
}
}
test-file.txt
/home/user/바탕화면/Maven-Quick-Start/practice/test-file.txt

상대적 패스와 절대 패스(NIO.2)

public class ExerciseFile {

public static void main(String[] args) {

File file = new File("test-file.txt");
Path path = Paths.get(file.toString());

System.out.println(path);

Path absolutePath = path.toAbsolutePath();

System.out.println(absolutePath);
}
}
test-file.txt
/home/user/바탕화면/Maven-Quick-Start/practice/test-file.txt

템프 파일 작성

기존의 어떤 파일명과도 중복되지 않는 일시적(임시) 파일을 자바에서 작성할 수 있다.

public class ExerciseFile {

public static void main(String[] args) throws IOException {

File file1 = File.createTempFile("temp", ".txt");

System.out.println("기본값일때");
System.out.println(file1);
System.out.println();

File directory = new File("tmpdir");
File file2 = File.createTempFile("temp", ".txt", directory);

System.out.println("디렉토리를 지정 했을 때");
System.out.println(file2);

}
}
기본값일때
/tmp/temp5154257285679472663.txt

디렉토리를 지정 했을 때
tmpdir/temp4462639435562126677.txt

디렉토리 지정일 경우에 디렉토리가 커런트 기준으로 만들어져 있지 않으면 IO 익셉션이 발생한다
그리고 기본값일때는 /tmp 에 저장된다(루트기준)
디렉토리 지정일 때는 커런트 기준으로 만들어둔 디렉토리에 저장된다

임시파일 작성의 NIO는 필요할 때 참고...

zip 으로 압축

public class ExerciseFile {

public static void main(String[] args) throws Exception {

// 압축 파일명을 지정
File zipFile = new File("compress.zip");

// 현재 있는지 확인
System.out.println(zipFile.exists());

// 작성 zip 파일용 출력 스트림
ZipOutputStream zos = new ZipOutputStream(new FileOutputStream(zipFile));

// 압축 대상
File targetFile = new File("test-file.txt");

// 파일 압축을 한다
ExerciseFile.compressFile(zos, targetFile);

// 작성한 zip 파일용 출력 스트림을 닫는다
zos.close();

// zip 압축후 존재 확인
System.out.println(zipFile.exists());
}

private static void compressFile(ZipOutputStream zos, File file) throws Exception {

byte[] buf = new byte[1024];
int len;

if (file.isDirectory()) {

// 대상이 디렉토리일 경우
// 작성하는 zip 파일에 디렉토리의 엔트리 설정을 한다
zos.putNextEntry(new ZipEntry(file.getPath() + "/"));

// 디렉토리 안의 파일을 취득한다
File[] childFileList = file.listFiles();

for (File childFile : childFileList) {
// 디렉토리 안의 파일들을 재귀적으로 불러온다
compressFile(zos, childFile);
}
} else {

// 대상이 파일일 경우
// 작성하는 zip 파일에 엔트리 설정을 한다
zos.putNextEntry(new ZipEntry(file.getPath()));

// 압축할 파일용 입력 스트림
BufferedInputStream bis = new BufferedInputStream(new FileInputStream(file));

// 압축 대상파일의 읽어들이면서
// zip 파일 출력스트림에 쓴다
while ((len = bis.read(buf, 0, buf.length)) != -1) {
zos.write(buf, 0, len);
}

// 압축파일용 입력 스트림을 닫는다
bis.close();

// 엔트리를 닫는다
zos.closeEntry();
}
}
}
false
true

ZipOutputStream 클래스를 사용하면 파일을 zip 으로 압축 하는것이 가능하다.
압축할 때에는 다음과 같은 순서로 한다

  • 쓰는 쪽 출력 스트림을 랩핑하는 형태로 zip 출력 스트림을 작성
  • ZipOutputStream.putNextEntry메서드로 zip 파일내의 엔트리명을 설정. 압축 대상이 디렉토리일 경우에는 디렉토리명의 마지막에 / 을 추가한 값을 엔트리명으로 설정한다
  • 압축 파일 내용을 쓴다 ZipOutputStream.write 메서드로 zip 출력스트림에 쓴다
  • 모든 쓰기가 종료되면 읽는쪽의 스트림을 닫고, ZipOutputStream.closeEntry 메서드로 엔트리도 닫는다
  • 위의 처리를 zip 압축할 파일 수 만큼 반복한다
  • 제일 마지막에 ZipOutputStream.close 메서드로 zip 출력 스트림을 닫는다.

zip 압축 풀기

public class ExerciseFile {

public static void main(String[] args) throws Exception {

byte[] buf = new byte[1024];
int len;

File zipFile = new File("compress.zip");

ZipInputStream zis = new ZipInputStream(new FileInputStream(zipFile));

ZipEntry zipEntry = null;

while((zipEntry = zis.getNextEntry()) != null) {
System.out.println(zipEntry);

File uncompressFile = new File(zipEntry.getName());

if (zipEntry.isDirectory()) {
uncompressFile.mkdirs();
} else {

BufferedOutputStream bos = new BufferedOutputStream(
new FileOutputStream(uncompressFile)
);

while ((len = zis.read(buf)) != -1) {
bos.write(buf, 0, len);
}

bos.close();
}
}
zis.close();
}
}
test-file.txt