본문 바로가기

Web/PHP

[PHP] 다중 파일을 zip으로 묶어받기

반응형

출처 : http://blog.bloodcat.com/277


php로 여러 개의 파일을 묶는 방법은 여러가지가 있다. exec()으로 압축 프로그램을 실행시켜서 묶거나 , ZipArchive로 묶거나, 직접 묶거나. 이 글은 다 묶어질 때까지 기다릴 필요가 없는 직접 묶는 방법으로 코딩한 소스를 공유하고자 작성하였다!

소스와 사용법

PKZip의 구조를 정리해놓은 문서와 PHP ZipArchive 클래스를 참고해서 직접 코딩했다. 이 소스는 zip 관리용이 아니라 묶어받기용이다.

<?php
class DirectZip
{
const BUFFER_SIZE = 4194304; // 4MiB
private $currentOffset;
private $entries;
public function open($filename)
{
set_time_limit(0);
ini_set('zlib.output_compression', 'Off');
header('Pragma: no-cache');
header('Expires: Thu, 01 Jan 1970 00:00:00 GMT');
header('Cache-Control: no-store');
header('Content-Type: application/zip');
header('Content-Disposition: attachment; ' .
'filename="' . $filename . '"; ' .
'filename*=UTF-8\'\'' . rawurlencode($filename));
$this->currentOffset = 0;
$this->entries = array();
}
public function addEmptyDir($dirname)
{
if ($this->addFile('php://temp', $dirname.'/') === false)
{
return false;
}
}
public function addFromString($localname, $contents)
{
$tmp = tempnam(sys_get_temp_dir(), __CLASS__);
$pointer = @fopen($tmp, 'wb');
if ($pointer === false)
{
@unlink($tmp);
return false;
}
fwrite($pointer, $contents);
$result = $this->addFile($tmp, $localname);
fclose($pointer);
@unlink($tmp);
if ($result === false)
{
return false;
}
}
public function addFile($filename, $localname = null)
{
$entry = new DirectZipEntry(empty($localname) ? basename($filename) : $localname, $this->currentOffset);
if ($entry->open($filename) === false)
{
return false;
}
$this->entries[] = $entry;
ob_start();
self::write(0x504B0304, 'N'); // sig entry
self::writeEntryStat($entry);
echo $entry->name;
$this->currentOffset += strlen(ob_get_flush());
while (!feof($entry->pointer))
{
$buffer = @fread($entry->pointer, self::BUFFER_SIZE);
echo $buffer;
flush();
$this->currentOffset += strlen($buffer);
}
$entry->close();
}
public function close()
{
ob_start();
foreach ($this->entries as $entry)
{
self::write(0x504B0102, 'N'); // sig index
self::write(0); // os: fat
self::writeEntryStat($entry);
self::write(0); // comment len
self::write(0); // disk # start
self::write(0); // internal attr
self::write(0, 'V'); // external attr
self::write($entry->offset, 'V');
echo $entry->name;
}
$length = strlen(ob_get_flush());
self::write(0x504B0506, 'N'); // sig end
self::write(0); // disk number
self::write(0); // disk # index start
self::write(count($this->entries)); // disk entries
self::write(count($this->entries)); // total entries
self::write($length, 'V');
self::write($this->currentOffset, 'V');
self::write(0); // comment len
flush();
}
private static function writeEntryStat($entry)
{
self::write(substr($entry->name, -1) == '/' ? 20 : 10);
self::write(2048); // flags: unicode filename
self::write(0); // compression: store
self::write($entry->mtime, 'V');
self::write($entry->crc, 'V');
self::write($entry->size, 'V'); // compressed size
self::write($entry->size, 'V'); // uncompressed size
self::write(strlen($entry->name));
self::write(0); // extra field len
}
private static function write($binary, $format = 'v')
{
echo pack($format, $binary);
}
}
class DirectZipEntry
{
public $offset;
public $pointer;
public $name;
public $crc;
public $size;
public $mtime;
public function __construct($name, $offset)
{
$this->offset = $offset;
$this->name = $name;
}
public function open($filename)
{
$this->pointer = @fopen($filename, 'rb');
if ($this->pointer === false)
{
return false;
}
list(, $this->crc) = unpack('N', hash_file('crc32b', $filename, true));
$fstat = fstat($this->pointer);
$this->size = $fstat['size'];
$mtime = $filename == 'php://temp' ? time() : $fstat['mtime'];
$this->mtime = (date('Y', $mtime) - 1980) << 25 | date('m', $mtime) << 21 | date('d', $mtime) << 16 |
date('H', $mtime) << 11 | date('i', $mtime) << 5 | date('s', $mtime) >> 1;
}
public function close()
{
fclose($this->pointer);
}
}
?>
view rawcla_directzip.php hosted with ❤ by GitHub
<?php
require 'cla_directzip.php';
$zip = new DirectZip();
$zip->open('브라우저로 보낼 압축파일 이름.zip');
$zip->addFile('/tmp/추가할 파일.jpg', '압축파일 내 파일 이름.jpg');
$zip->addEmptyDir('압축파일 내 폴더 이름'); // 안 해도 상관없음.
$zip->addFile('/tmp/추가할 파일2.png', '압축파일 내 폴더 이름/압축파일 내 파일 이름.png');
$zip->addFile('/tmp/추가할 파일3.jpg'); // 압축파일에 파일을 '추가할 파일3.jpg'로 추가
$zip->addFromString('바로 글쓰기.txt', '파일 내용'); // 압축파일에 '파일 내용'을 '바로 글쓰기.txt'로 추가
$zip->close();
/*
브라우저로 보낼 압축파일 이름.zip
│ 바로 글쓰기.txt
│ 압축파일 내 파일 이름.jpg
│ 추가할 파일3.jpg
└─압축파일 내 폴더 이름
압축파일 내 파일 이름.png
*/
?>


반응형