zoukankan      html  css  js  c++  java
  • 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

  • 相关阅读:
    第十一章关联容器
    第十章泛型算法
    第九章
    第八章
    阅读记录
    java.lang.Class阅读笔记
    java.time包阅读笔记
    CLion运行多个main函数
    c++中lower_bound和upper_bound中的comp参数
    如何写dfs
  • 原文地址:https://www.cnblogs.com/mingao/p/7466974.html
Copyright © 2011-2022 走看看