public function addFileFromStream($name, $stream, $opt = array())
{
$block_size = 1048576;
// process in 1 megabyte chunks
$algo = 'crc32b';
$meth = 0x0;
$genb = 0x8;
$crc = $zlen = $len = 0;
$hash_ctx = hash_init($algo);
// send local file header.
$num_bytes_written = $this->addFileHeader($name, $opt, $meth, $crc, $zlen, $len, $genb);
$readError = false;
// Read data in chunks and send it to the output as soon as it comes in.
while (!feof($stream)) {
// Read and send.
$data = fread($stream, $block_size);
// Exist if fread failed, and flag the error for post-handling.
if ($data === false) {
$readError = true;
break;
}
$this->send($data);
// Update crc and data lengths.
hash_update($hash_ctx, $data);
$len += strlen($data);
$zlen += strlen($data);
}
// Calculate the actual crc.
$crc = hexdec(hash_final($hash_ctx));
// Send the data descriptor right after sending the data content.
// Now we know the actual content len, zlen and crc.
$data_descriptor_len = $this->addDataDescriptorHeader($len, $zlen, $crc);
// Calculate how many bytes have been written in total.
$num_bytes_written += $zlen + $data_descriptor_len;
// add to central directory record and increment offset
$this->addToCdr($name, $opt, $meth, $crc, $zlen, $len, $num_bytes_written, $genb);
// If there was a read error, we still want the cdr to be appended, this way we ensure
// the zip file can be opened if the exception thrown below is handled by the invoker.
// E.g, in the catch block, we could add an explanatory 'ERROR.txt' so the enduser knows
// something went wrong. If this error handling is not performed, the end user can potentially
// end with a valid zip file but with incomplete or missing files.
if ($readError) {
throw new StreamNotReadableException($name);
}
}