Skip to content
isdnetworks
Go back

압축을 풀었더니 파일명이 깨졌다

주문 자료를 엑셀로 만들어 ZIP 으로 묶어 내려받게 했고 파일명에 상품명이 들어갔다. 받아서 풀면 파일명이 깨진다는 문의가 왔다.

Table of contents

Open Table of contents

증상 — 파일이 아니라 해석이 달랐다

서버에서 만든 압축 파일부터 확인했다.

$ unzip -l order_20140205.zip
   Length      Date    Time    Name
     15420  02-05-2014 09:12   íì´ì´_주문ë´ì­.xlsx

리눅스에서 풀어도 깨진다. 만들 때부터 깨진 것으로 보였다. 그런데 인코딩을 지정해서 다시 뽑으니 정상이었다.

$ unzip -O cp949 -l order_20140205.zip
     15420  02-05-2014 09:12   타이어_주문내역.xlsx

파일이 깨진 것이 아니라 푸는 쪽이 인코딩을 짐작하고 있었다.

ZIP 은 파일명 바이트만 담고 어느 인코딩인지는 담지 않는다. 그래서 윈도우 기본 도구는 시스템 기본 인코딩으로, 리눅스 도구는 UTF-8 로 해석한다. 만든 쪽이 무엇으로 썼는지 알릴 자리가 없었다.

표시 비트가 있었지만

ZIP 에는 파일명이 UTF-8 이라고 표시하는 비트가 있다. 그것을 켜면 푸는 쪽이 짐작하지 않아도 된다.

$zip = new ZipArchive();
$zip->open($path, ZipArchive::CREATE);
$zip->addFile($src, $name);

문제는 ZipArchive 가 기본으로는 그 비트를 안 켠다는 점이었다. 우리가 쓰는 판에는 그것을 켜는 설정 자체가 없었다. 라이브러리를 바꾸는 것은 이 문제 하나로 하기에는 컸다.

압축 안 이름을 영문으로 바꿨다

가장 확실한 방법은 ZIP 안의 파일명에 한글을 안 쓰는 것이었다.

$name = 'order_' . $orderNo . '_' . date('Ymd') . '.xlsx';

주문 번호와 날짜로 이름을 만드니 어느 도구로 풀어도 안 깨졌다. 상품명은 파일 안의 내용으로 넣었다.

파일 안의 내용은 인코딩 문제가 없어서 한글을 그대로 쓸 수 있었다. 이름은 기계가 다루고 내용은 사람이 본다는 것으로 나눈 셈이다. 이 구분이 다른 자리에서도 그대로 쓸 만했다.

내려받기 이름도 같은 문제였다

브라우저에 알려 주는 이름에도 같은 문제가 있었다.

header('Content-Disposition: attachment; filename="' . $filename . '"');

여기에 한글을 넣으면 브라우저마다 다르게 처리한다. 그래서 두 가지를 같이 보냈다.

$fallback = 'order_' . date('Ymd') . '.zip';
$utf8 = rawurlencode('주문내역_' . date('Ymd') . '.zip');

header("Content-Disposition: attachment; filename=\"{$fallback}\"; filename*=UTF-8''{$utf8}");

앞의 filename 은 옛 브라우저용 영문 이름이고 뒤의 filename* 이 인코딩을 명시한 이름이다. 규격상 둘 다 알아듣는 브라우저는 filename* 쪽을 쓴다.

확인할 환경도 목록으로 적었다.

만드는 쪽    서버 (리눅스, UTF-8)
푸는 쪽
  - 윈도우 기본 도구
  - 윈도우 별도 압축 프로그램
  - 맥 기본 도구
  - 리눅스 명령줄

넷 다 확인했고 영문 파일명으로 바꾼 뒤에는 전부 정상이었다. 한 환경에서만 확인하면 다른 환경의 문제를 못 본다.

파일명에 한글이 들어가는 다른 자리도 찾았다.

$ grep -rn "filename\|->addFile\|move_uploaded_file" --include=*.php application/

업로드 저장과 로그 파일명은 이미 영문이었고 메일 첨부에 같은 문제가 있어서 인코딩을 명시하는 방식으로 함께 고쳤다.

정리


Share this post on:

Previous Post
큐를 넣을 자리인지 다시 봤다
Next Post
몰아 보내면 막힌다