2025. 7. 2. 23:31ㆍBackend/Spring

특정 데이터를 파일(.tar)로 압축할 때 생길 수 있는 에러에 관한 글입니다.
tar는 tape archive의 약자로, 파일 형식 혹은 명령어를 의미합니다. tar는 여러 파일이나 디렉토리를 묶을 때 사용하는데요. 놀라운 건 tar는 zip 등과 달리 파일 압축을 하지는 않습니다. tar를 만들기 위해 spring 에서 TarArchiveEntry를 사용할 수 있습니다. TarArchiveEntry는 파일(또는 디렉토리)에 대한 메타데이터를 헤더에 기록하게 됩니다.
리눅스에서 archive로 묶는 tar를 생성하고 싶다면
| 리눅스 명령어 | |
| 파일(또는 디렉토리) archive | tar -cvf [생성파일.tar] [디렉토리 or 파일] |
| 파일(또는 디렉토리) archive + 압축 | tar -czvf [생성파일.tar] [디렉토리 or 파일] |
| 파일(또는 디렉토리) archive 해제 | tar -xvf [생성파일.tar] |
| 파일(또는 디렉토리) archive + 압축 해제 | tar -xzvf [생성파일.tar] |
TarArhiveEntry를 이용하여 tar를 만들고자 한다면, 다음과 같은 라이브러리를 추가해줘야 합니다.
| implementation 'org.apache.commons:commons-compress' // 버전 생략 |
GPT를 활용하여 확인한 예시 코드입니다.
File tar = new File("test.tar");
File file = new File("sample.txt");
try (
FileOutputStream fos = new FileOutputStream(outputTar);
BufferedOutputStream bos = new BufferedOutputStream(fos);
TarArchiveOutputStream tarOut = new TarArchiveOutputStream(bos)) {
TarArchiveEntry tarArhiveEntry = new TarArchiveEntry(file, file.getName());
tarArhiveEntry.setSize(file.length());
tarOut.putArchiveEntry(tarArhiveEntry);
//코드 생략 (file을 write하거나 copy하거나)
tarOut.closeArchiveEntry();
}
//이하 생략
위 코드에서 주요 특징은 다음과 같습니다.
1) TarArchiveOutputStream : .tar 파일에 데이터를 작성할 때 사용하는 stream
2) TarArchiveEntry : tar 에 각 파일의 메타데이터를 가지고 있는 Entry
😂원인
| Caused by: java.io.IOException: entry 'sample.txt' closed at '0' before the '10000' bytes specified in the header were written |
TarArchiveEntry를 이용하게 되면, 위와 같은 에러가 간혹 발생할 수 있는데요!
에러가 발생했던 원인은, TarArchiveEntry를 이용하여 파일 크기를 명시하게 되는데, 명시된 사이즈와 TarArchiveEntry를 close 할 때 확인되는 파일 크기와 달랐기 때문입니다.
tarOut.putArchiveEntry()를 통해 tar 파일에 기록된 파일 크기가 10000 bytes였으나, tarOut.closeArchiveEntry() 에서는 지금까지 사용한 bytes가 0이었다는 겁니다. 이렇게 크기가 차이가 날 경우에 IOException이 발생하게 됩니다.
추가로,
| Caused by: java.io.IOException: request to write '@@@' bytes exceeds size in header of '10000' bytes for entry 'sample.txt' |
다음과 같은 에러가 발생한다면, putArchiveEntry() 시점에서는 파일 크기가 10000 bytes였으나, closeArchiveEntry() 시점에서는 훨씬 큰 파일 크기가 사용되었음을 확인해서 IOException이 발생하게 됩니다.
| put(10000) > close(0) | entry 'sample.txt' closed at '0' before the '10000' bytes specified in the header were written |
| put(10000) < close(more) | equest to write '@@@' bytes exceeds size in header of '10000' bytes for entry 'sample.txt' |
📝해결
tar 파일로 만든다는 건, 기존에 계속 업데이트되는 파일(또는 디렉토리)이 존재함을 가정합니다.
그래야 업데이트되는 파일들을 붙이고 모아서 tar 파일로 만들 수 있고, 해당 tar 파일을 클라이언트에 제공할 수 있을 테니까요.
그래서 개인적으로 고민했던 몇 가지 방안을 아래에 적어봤습니다.
1. 클라이언트 측의 코드 수정
파일이 업데이트되는 찰나에, 파일의 크기가 0이 될 수 있습니다. 서버 측에서는 파일을 생성해놓고 기록하는 중인데, 클라이언트에서는 파일을 가져가려고 한 겁니다.
그래서 사이즈가 0인 경우, 한 번 더 요청할 수 있게 전략을 수정하면 좋을 것 같습니다.
2. 서버 측 코드 수정
반대로, 서버측에서는 클라이언트가 가져가는 파일의 크기가 0이 되지 않도록, 파일 생성 전략을 고민해야 될 것으로 파악됩니다.
예를 들어, 1개의 파일만 가져갈 수 있다고 한다면, 임시 디렉토리를 만들어 tar 파일을 미리 생성해주는 겁니다.
또는 기존 파일(예: A.txt)에서 업데이트된 파일(A.txt.tmp)을 생성하고, renameTo()를 이용하여 빠르게 교체하는 방법이 있을 것으로 파악됩니다.
틀린 정보가 있다면, 말씀해주시면 감사하겠습니다😊
'Backend > Spring' 카테고리의 다른 글
| [Spring] Rest 통신으로 공공 API 연결해보기 (0) | 2025.08.19 |
|---|---|
| [Error/Spring] out of START_ARRAY token (0) | 2025.06.15 |
| [Error/Spring/SSL] PKIX path building failed (0) | 2025.05.31 |
| [Spring/AI] Spring AI를 활용하여 가볍게 영작하기 (0) | 2025.03.24 |
| [Spring/Architecture] Rate Limiter (처리율 제한 장치) - too many requests (1) | 2025.03.23 |