mirror of
https://github.com/splitbrain/php-archive.git
synced 2025-01-16 21:18:26 +01:00
Incremental block by block compression for ZIP
This commit is contained in:
parent
726dfddda2
commit
5a132129e9
124
src/Zip.php
124
src/Zip.php
@ -295,13 +295,88 @@ class Zip extends Archive
|
||||
throw new ArchiveIOException('Archive has been closed, files can no longer be added');
|
||||
}
|
||||
|
||||
$data = @file_get_contents($file);
|
||||
if ($data === false) {
|
||||
$fp = @fopen($file, 'rb');
|
||||
if ($fp === false) {
|
||||
throw new ArchiveIOException('Could not open file for reading: '.$file);
|
||||
}
|
||||
|
||||
// FIXME could we stream writing compressed data? gzwrite on a fopen handle?
|
||||
$this->addData($fileinfo, $data);
|
||||
$offset = $this->dataOffset();
|
||||
$name = $fileinfo->getPath();
|
||||
$time = $fileinfo->getMtime();
|
||||
|
||||
// write local file header (temporary CRC and size)
|
||||
$this->writebytes($this->makeLocalFileHeader(
|
||||
$time,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
$name,
|
||||
(bool) $this->complevel
|
||||
));
|
||||
|
||||
// we store no encryption header
|
||||
|
||||
// prepare info, compress and write data to archive
|
||||
$deflate_context = deflate_init(ZLIB_ENCODING_DEFLATE, ['level' => $this->complevel]);
|
||||
$crc_context = hash_init('crc32b');
|
||||
$size = $csize = 0;
|
||||
|
||||
while (!feof($fp)) {
|
||||
$block = fread($fp, 512);
|
||||
|
||||
if ($this->complevel) {
|
||||
$is_first_block = $size === 0;
|
||||
$is_last_block = feof($fp);
|
||||
|
||||
if ($is_last_block) {
|
||||
$c_block = deflate_add($deflate_context, $block, ZLIB_FINISH);
|
||||
// get rid of the compression footer
|
||||
$c_block = substr($c_block, 0, -4);
|
||||
} else {
|
||||
$c_block = deflate_add($deflate_context, $block, ZLIB_NO_FLUSH);
|
||||
}
|
||||
|
||||
// get rid of the compression header
|
||||
if ($is_first_block) {
|
||||
$c_block = substr($c_block, 2);
|
||||
}
|
||||
|
||||
$csize += strlen($c_block);
|
||||
$this->writebytes($c_block);
|
||||
} else {
|
||||
$this->writebytes($block);
|
||||
}
|
||||
|
||||
$size += strlen($block);
|
||||
hash_update($crc_context, $block);
|
||||
}
|
||||
fclose($fp);
|
||||
|
||||
// update the local file header with the computed CRC and size
|
||||
$crc = hexdec(hash_final($crc_context));
|
||||
$csize = $this->complevel ? $csize : $size;
|
||||
$this->writebytesAt($this->makeCrcAndSize(
|
||||
$crc,
|
||||
$size,
|
||||
$csize,
|
||||
), $offset + self::LOCAL_FILE_HEADER_CRC_OFFSET);
|
||||
|
||||
// we store no data descriptor
|
||||
|
||||
// add info to central file directory
|
||||
$this->ctrl_dir[] = $this->makeCentralFileRecord(
|
||||
$offset,
|
||||
$time,
|
||||
$crc,
|
||||
$size,
|
||||
$csize,
|
||||
$name,
|
||||
(bool) $this->complevel
|
||||
);
|
||||
|
||||
if(is_callable($this->callback)) {
|
||||
call_user_func($this->callback, $fileinfo);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@ -709,6 +784,29 @@ class Zip extends Archive
|
||||
return $written;
|
||||
}
|
||||
|
||||
/**
|
||||
* Write to the open filepointer or memory at the specified offset
|
||||
*
|
||||
* @param string $data
|
||||
* @param int $offset
|
||||
* @throws ArchiveIOException
|
||||
* @return int number of bytes written
|
||||
*/
|
||||
protected function writebytesAt($data, $offset) {
|
||||
if (!$this->file) {
|
||||
$this->memory .= substr_replace($this->memory, $data, $offset);
|
||||
$written = strlen($data);
|
||||
} else {
|
||||
@fseek($this->fh, $offset);
|
||||
$written = @fwrite($this->fh, $data);
|
||||
@fseek($this->fh, 0, SEEK_END);
|
||||
}
|
||||
if ($written === false) {
|
||||
throw new ArchiveIOException('Failed to write to archive stream');
|
||||
}
|
||||
return $written;
|
||||
}
|
||||
|
||||
/**
|
||||
* Current data pointer position
|
||||
*
|
||||
@ -825,6 +923,8 @@ class Zip extends Archive
|
||||
return $header;
|
||||
}
|
||||
|
||||
const LOCAL_FILE_HEADER_CRC_OFFSET = 14;
|
||||
|
||||
/**
|
||||
* Returns a local file header for the given data
|
||||
*
|
||||
@ -865,6 +965,22 @@ class Zip extends Archive
|
||||
return $header;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns only a part of the local file header containing the CRC, size and compressed size.
|
||||
* Used to update these fields for an already written header.
|
||||
*
|
||||
* @param int $crc CRC32 checksum of the uncompressed data
|
||||
* @param int $len length of the uncompressed data
|
||||
* @param int $clen length of the compressed data
|
||||
* @return string
|
||||
*/
|
||||
protected function makeCrcAndSize($crc, $len, $clen) {
|
||||
$header = pack('V', $crc); // crc-32
|
||||
$header .= pack('V', $clen); // compressed size
|
||||
$header .= pack('V', $len); // uncompressed size
|
||||
return $header;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an allowed filename and an extra field header
|
||||
*
|
||||
|
Loading…
x
Reference in New Issue
Block a user