본문 바로가기

파일 형식 백과사전

ZIP이 손상되었다고 뜰 때, 어디가 깨졌는지부터 확인하면 살릴 수 있습니다

메일로 받은 압축 파일을 열었더니 손상되었다는 메시지만 뜨고 안이 보이지 않습니다. 보낸 쪽에 다시 요청하는 것 말고 방법이 없어 보입니다.

그런데 ZIP은 통째로 하나가 아니라 파일마다 따로 담기고 맨 끝에 목록이 붙는 구조라, 깨진 자리에 따라 살릴 수 있는 양이 완전히 달라집니다. 정상 ZIP의 바이트를 위치별로 직접 망가뜨려 증상이 어떻게 갈리는지 재현해 봤습니다.

 

파일 4개를 담은 2,554바이트 ZIP의 구조 도식과 손상 위치별 증상 표. A는 02번 파일의 압축 데이터가 깨진 경우로 목록은 보이지만 그 파일만 못 꺼내고 4개 중 3개를 살린다. B는 03번 파일 앞머리 표식이 깨진 경우로 testzip이 03_메모.txt를 지목하고 3개를 살린다. C는 맨 끝 파일 목록이 깨진 경우로 목록조차 안 열리지만 앞쪽 데이터가 멀쩡해 4개 모두 CRC 검사를 통과하며 꺼낼 수 있다

 

ZIP은 앞에 파일, 맨 끝에 목록입니다

ZIP 안에는 파일이 하나씩 순서대로 들어갑니다. 각 파일 앞에는 이름과 크기, 검사값을 적은 짧은 머리말이 붙고, 압축된 내용이 그 뒤에 이어집니다.

그리고 맨 끝에 전체 목록이 한 번 더 붙습니다. 압축 프로그램이 파일을 열자마자 안에 무엇이 들었는지 바로 보여 줄 수 있는 것은 이 목록 덕분입니다.

중요한 점은 이 목록이 파일의 에 있다는 것입니다. 전송이 중간에 끊기거나 저장 공간이 모자라 파일이 잘리면, 가장 먼저 사라지는 부분이 바로 목록입니다.


깨진 자리에 따라 증상이 다릅니다

텍스트 파일 4개를 담은 2,554바이트짜리 ZIP을 만들고, 세 지점의 바이트를 각각 뒤집어 봤습니다.

깨뜨린 곳 증상 꺼낼 수 있는 파일
02번 파일의 압축 데이터 목록은 보이는데 그 파일만 안 나옴 3개 / 4개
03번 파일의 앞머리 표식 검사 기능이 그 파일 이름을 지목 3개 / 4개
맨 끝 파일 목록 목록조차 열리지 않음 4개 / 4개

주목할 곳은 마지막 줄입니다. 겉으로 가장 심각해 보이는 경우가 실제로는 내용이 전부 멀쩡한 경우였습니다. 목록만 날아갔을 뿐 앞쪽 파일 데이터는 손대지 않았기 때문입니다.

 

어느 파일이 깨졌는지 이름으로 확인하기

ZIP은 파일마다 검사값(CRC)을 함께 저장합니다. 꺼낸 내용으로 계산한 값과 저장된 값이 다르면 그 파일이 상한 것입니다. 파이썬 표준 기능인 testzip이 이 검사를 처음부터 끝까지 돌려 가장 먼저 어긋나는 파일 이름을 돌려줍니다.

import zipfile

z = zipfile.ZipFile("받은파일.zip")
print(z.testzip())      # 이상 없으면 None, 깨졌으면 파일 이름

# 실제 결과
# 정상 ZIP        -> None
# 03번 헤더 훼손   -> 03_메모.txt

이름을 알면 판단이 쉬워집니다. 정작 필요한 자료가 멀쩡한 쪽에 있다면 다시 받을 필요가 없고, 그 파일 하나만 따로 요청하면 됩니다.

파일별로 하나씩 확인하고 싶다면 꺼내 보면서 걸러도 됩니다.

ok, ng = [], []
for name in z.namelist():
    try:
        z.read(name)
        ok.append(name)
    except Exception:
        ng.append(name)

print("살아 있음:", ok)
print("깨짐:", ng)

목록이 깨져 열리지도 않을 때

목록이 상하면 압축 프로그램은 파일을 아예 열지 못합니다. 하지만 앞쪽에는 파일들이 그대로 남아 있습니다. 각 파일 앞머리에는 50 4B 03 04라는 표식이 붙어 있으니, 이것을 찾아가며 하나씩 풀어내면 됩니다.

import struct, zlib

raw = open("받은파일.zip", "rb").read()
pos = 0
while True:
    i = raw.find(b"PK\x03\x04", pos)
    if i < 0:
        break
    pos = i + 4
    hdr = struct.unpack("<HHHHHIIIHH", raw[i + 4:i + 30])
    method, crc, csize, nlen, elen = hdr[2], hdr[5], hdr[6], hdr[8], hdr[9]
    name = raw[i + 30:i + 30 + nlen].decode("utf-8", "replace")
    blob = raw[i + 30 + nlen + elen:][:csize]
    data = zlib.decompress(blob, -15) if method == 8 else blob
    print(name, len(data), (zlib.crc32(data) & 0xFFFFFFFF) == crc)

목록만 깨진 파일에 이 방법을 써 보니 파일 4개가 모두 나왔고 검사값도 전부 일치했습니다. 압축 데이터가 깨진 파일에서는 그 하나만 건너뛰고 나머지 3개가 나왔습니다.

압축 프로그램 중에도 목록이 없을 때 앞에서부터 훑어 복구를 시도하는 기능을 가진 것이 있습니다. 다만 어떤 방법이든 이미 사라진 바이트를 되살리지는 못하니, 원본을 다시 받을 수 있으면 그쪽이 언제나 확실합니다.

 

자주 묻는 질문

Q. 왜 전송하다 깨지나요?
파일이 끝까지 다 오지 않은 채로 저장되는 경우가 가장 흔합니다. 이때 잘려 나가는 부분이 파일 끝의 목록이라 열리지 않습니다. 받은 파일 크기가 보낸 쪽과 같은지 먼저 비교해 보세요.

Q. 압축 프로그램마다 결과가 다릅니다.
목록이 없을 때 앞에서부터 훑을지, 바로 포기할지가 프로그램마다 다르기 때문입니다. 파일이 달라진 것이 아니라 대응 방식이 다른 것이며, 한글 파일명이 깨져 보이는 문제도 같은 이유로 프로그램마다 결과가 갈립니다.

Q. 깨진 파일을 반만 꺼내 쓸 수 있나요?
압축된 데이터는 앞에서부터 풀어 나가는 방식이라, 깨진 지점 앞까지만 나오고 그 뒤는 나오지 않습니다. 텍스트라면 앞부분이라도 건질 수 있지만 문서나 사진은 열리지 않는 경우가 많습니다.

Q. 애초에 덜 깨지게 하려면요?
큰 파일은 조각으로 나눠 보내면 한 조각이 잘못돼도 그 조각만 다시 받으면 됩니다. 형식별 특성은 ZIP과 7Z, TAR.GZ 비교에 정리해 두었습니다.

 

 

파일이 안 열릴 때 정말 상한 것인지 다른 이유인지 가려내는 접근은 문서 쪽에서도 같습니다. 엑셀 파일이 손상된 건지 확장자만 다른 건지 구분하는 방법도 파일 안을 열어 확인하는 순서로 풀었습니다.

 

엑셀 파일이 안 열릴 때, 손상된 건지 확장자만 다른 건지 구조로 구분하기

거래처에서 받은 xlsx 파일을 열었더니 "파일이 손상되어 열 수 없습니다" 창이 뜨는 상황, 다들 한 번쯤 겪어 보셨을 겁니다. 그런데 이 메시지가 뜨는 이유는 하나가 아닙니다. 진짜로 파일이 깨

richyeon.com

 

 

위 증상과 살린 파일 수는 윈도우 11에서 파이썬 3.14.3으로 정상 ZIP을 만든 뒤 지정한 위치의 바이트를 뒤집어 실제로 재현하고, 표준 zipfile로 열어 확인한 결과입니다.

 

 

행은 3천 개뿐인데 엑셀 파일이 3.4MB, 용량을 먹는 범인을 파일 안에서 찾아내기

엑셀 파일이 무거워지면 대개 데이터가 많아서라고 생각하게 됩니다. 하지만 실제로는 데이터 양과 파일 용량이 따로 노는 경우가 훨씬 흔합니다.3,000행짜리 주문 내역을 실제로 만들어 저장해

richyeon.com

 

 

움짤(GIF) 용량이 유독 커요, 프레임과 색상 수로 줄이는 법

사진 몇 장을 이어 붙여 움짤을 만들었는데, 정지 사진 하나보다 용량이 몇 배나 커진 경험 있으신가요. 카카오톡으로 보내려니 전송이 느리고, 커뮤니티에 올리려니 용량 제한에 걸리기도 합니

richyeon.com