php打包文件夹

因公司业务需要,需要将学生上传的附件进行归类, 再将学生归到班级, 将班级归到年级,最后整合到学校一层。我们的客户是老师,不可能让他们自己一个附件一个附件的下载查看,那估计用户得崩溃。

现在基本上都用云存储了, 我们用的是七牛云。七牛提供了多文件压缩,但是下载之后解压缩会导致中文文件名乱码,向七牛官方反应过,但没有下文。那只能自己实现了。

php自带ZipArchive 类,打包就靠这个了。最简单的就是打包单文件而非文件夹, 先从简单的来。先确保zip扩展已加载,确保打包的源文件(夹)存在, 否则退出

if (!extension_loaded('zip') || !file_exists($source)) {
    return false;
}

开始实例化ZipArchive

$zip = new ZipArchive();

打开 zip file archive

if (!$zip->open($destination, ZipArchive::CREATE)) { // 如果不存在则创建一个zip压缩包。
    return false;
}

注意open方法的flags参数

  • ZIPARCHIVE::CREATE (integer) 如果不存在则创建一个zip压缩包。
  • ZIPARCHIVE::OVERWRITE (integer) 总是以一个新的压缩包开始,此模式下如果已经存在则会被覆盖。
  • ZIPARCHIVE::EXCL (integer) 如果压缩包已经存在,则出错。
  • ZIPARCHIVE::CHECKCONS (integer) 对压缩包执行额外的一致性检查,如果失败则显示错误。

打包单文件

$zip->addFromString(basename($source), file_get_contents($source));

在打包文件夹之前, 我们需要遍历文件夹,并把文件夹添加到压缩包。在遍历这一步, 我发现很多中文的资料还是采用很古老的方式, 而php早就提供了内置类,这里需要用到两个类The RecursiveDirectoryIterator classThe RecursiveIteratorIterator class
RecursiveDirectoryIterator提供递归遍历文件系统目录的接口,RecursiveIteratorIterator可用于通过递归迭代器进行遍历,我们开始吧

$files = new RecursiveIteratorIterator(new RecursiveDirectoryIterator($source), RecursiveIteratorIterator::SELF_FIRST);

注意RecursiveIteratorIterator构造函数的mode参数

  • RecursiveIteratorIterator::LEAVES_ONLY - The default. Lists only leaves in iteration.
  • RecursiveIteratorIterator::SELF_FIRST - Lists leaves and parents in iteration with parents coming first.
  • RecursiveIteratorIterator::CHILD_FIRST - Lists leaves and parents in iteration with leaves coming first.

我们选择RecursiveIteratorIterator::SELF_FIRST,因为需要保留目录。目前为止,我们还缺少一个将目录添加到压缩包的函数,php官方自带ZipArchive::addEmptyDir,下面是打包文件夹的代码

foreach ($files as $file)
{
    $file = str_replace('\', '/', $file);
    // 忽略 "." 和 ".." 目录
    if( in_array(substr($file, strrpos($file, '/')+1), array('.', '..')) )
        continue;

    $file = realpath($file);

    if (is_dir($file) === true)
    {
        $zip->addEmptyDir(str_replace($source . '/', '', $file . '/')); // 新建目录
    }
    else if (is_file($file) === true)
    {
        $zip->addFromString(str_replace($source . '/', '', $file), file_get_contents($file)); // 新建文件
    }
}

到这就基本完成了,下面是完整的打包函数

/** 递归打包文件夹
* @param $source
* @param $destination
* @return bool
*/
function Zip($source, $destination)
{
    if (!extension_loaded('zip') || !file_exists($source)) {
        return false;
    }

    $zip = new ZipArchive();
    if (!$zip->open($destination, ZipArchive::CREATE)) {
        return false;
    }

    $source = str_replace('\', '/', realpath($source));

    if (is_dir($source) === true)
    {
        $files = new RecursiveIteratorIterator(new RecursiveDirectoryIterator($source), RecursiveIteratorIterator::SELF_FIRST);

        foreach ($files as $file)
        {
            $file = str_replace('\', '/', $file);

            // Ignore "." and ".." folders
            if( in_array(substr($file, strrpos($file, '/')+1), array('.', '..')) )
                continue;

            $file = realpath($file);

            if (is_dir($file) === true)
            {
                $zip->addEmptyDir(str_replace($source . '/', '', $file . '/'));
            }
            else if (is_file($file) === true)
            {
                $zip->addFromString(str_replace($source . '/', '', $file), file_get_contents($file));
            }
        }
    }
    else if (is_file($source) === true)
    {
        $zip->addFromString(basename($source), file_get_contents($source));
    }

    return $zip->close();
}  

再额外讲一些,我们将打包好的文件夹回传到七牛供用户下载, 或者放到自己服务器供用户下载。而在下载这里, 我发现很多文章还是用php读取的方式, 这样真的很浪费资源, 这里我们可以借助nginx的XSendfile, 有兴趣的同学可以试试,可以参考这篇文章使用X-Sendfile机制加快PHP下文件的下载。另外一个问题就是打包时间可能过长, 我们可以提前告知用户正在打包, 打包在后台进行, 大家可以参考fastcgi_finish_request

原文地址:https://www.cnblogs.com/mingao/p/7466974.html