PHP遍历文件夹并按筛选结果压缩下载

需求:将指定路径下的文件夹及文件筛选出符合条件的文件并打包下载。

注意:php的mvc结构可以通过控制器添加header头去下载,但是通过前端Ajax请求接口的话就不能用此方法,因为不管是json数据还是二进制文件都会被浏览器当成字符串处理。

并且,header下载完可以直接unlink删除压缩包,js的话没办法立即删除,只能回调成功下载文件后在调用删除接口,或者可以将压缩的文件名写到数据库,然后在写一个方法去执行数据库逻辑删除,或者crontab定时物理删除,或者干脆不管它,反正每次压缩前会先检查是否存在执行删除操作。等等。

1、Ajax调用的控制器接口,基于laravelC

public function download(Request $request){  
      $data = $request->json()->all();
      $uuid = $data['uuid'];
      $source_file_path = "./upload/".$uuid;  //需要遍历的文件夹地址
      if(!is_dir($source_file_path)){  //检查目录是否存在
            return response()->json(['status' => '403']);
      }
      $des_file_path = storage_path("zip/".$uuid.".zip"); //目的路径

      if(file_exists($des_file_path)){  //重新生成文件
           unlink($des_file_path);
      }
      $zip=new \ZipArchive();
      if($zip->open($des_file_path, \ZipArchive::OVERWRITE) === TRUE){
           $this->addFileToZip($source_file_path, $zip); //调用方法,对要打包的根目录进行操作,并将ZipArchive的对象传递给方法
           $zip->close(); //关闭处理的zip文件
           if(!file_exists($des_file_path)){
                return response()->json(['status' => '404','msg'=>'没有查询到符合条件的文件']);
                //exit('无法找到文件'); //即使创建,仍有可能失败  比如没有符合条件的file,就没有addfile,如果你遍历文件夹没有添加过滤条件,则可以忽略此if判断
            }

//如果不是Ajax请求,可以直接通过注释的header去下载
//                header("Cache-Control: public");
//                header("Content-Description: File Transfer");
//                header("Content-Type: application/zip");     //zip格式的
//                header("Content-Transfer-Encoding: Binary"); //告诉浏览器,这是二进制文件
//                header('Content-Disposition: attachment; filename='.basename($des_file_path)); //文件名

//                header('Content-Length:'.filesize($des_file_path)); //告诉浏览器,文件大小
//                @readfile($des_file_path);
//                unlink($des_file_path);删除压缩包
//                exit;
        }else{
             exit('无法打开文件,或者文件创建失败');
        }
       
        $download_file_path = "/Project/storage/zip/".$uuid.".zip";//js回掉的文件路径
        return response()->json(['url' => $download_file_path],200,[],JSON_UNESCAPED_SLASHES);
}

2、控制器用到的addFileToZip方法(遍历文件夹),可以写到助手函数里面去

public function addFileToZip($path,$zip){
        //定义一个数组,可以打印调试,列出所有符合条件的文件
        //$files = array();

        $handler=opendir($path); //打开当前文件夹由$path指定。
        while(($file = readdir($handler)) !== false){
            if($file != "." && $file != ".."){ //文件夹文件名字为'.'和‘..',不要对他们进行操作
                if(is_dir($path."/".$file)){ // 如果读取的某个对象是文件夹,则递归
                    $this->addFileToZip($path."/".$file, $zip);
                }else{ //将文件加入zip对象
                 //此处else写你想要过滤文件的条件代码,比如根据时间戳做一些操作,filectime 
//                        $files[] = $path . "/" . $file; 
                        $zip->addFile($path."/".$file,$file);  //将遍历的文件添加到要压缩的zip里,第二个参数指定文件所在的文件夹名local_name(最终生成的文件名,如果本身文件就是zip包,再压缩需要加zip后缀)

//                    $path_arr = explode("/",$path);
//                    $prefix_file = $path_arr[count($path_arr)-1];
//                    $zip->renameName($path."/".$filename,$prefix_file.'/'.$file);//$prefix_file是指定压缩文件所在的文件夹名,不要的话,表示所有的文件同级,renameName等同于addFile方法的第二个参数。如果你的路径文件夹是最后一级(即目录下全是文件,无目录),就忽略前缀$prefix_file
                }
            }
        }
        @closedir($path);
    }

3、前端调用接口

$.ajax({
       type:"POST",
       url:"download",//你的路由地址
       dataType:"json",
       async: false,
       data:JSON.stringify({"uuid":123456789}),
       success:function (result) {
               if("404"==result.status){
                    alert(result.msg);
               else{
                    window.open("" +result.url);
//                  window.location.href=result.url
               }
       }
})

备注:为什么js不能用header去下载?看这儿    情况和他描述的并无二致。

问题:

有时候open()方法会报错,我在php5.5版本使用正常,php7.2反而报错,返回状态码 9,表示没有此文件。具体原因未知。

\ZipArchive::open($file_path, \ZipArchive::OVERWRITE)

查询php.net官方Link,提供两种办法,其实结果应该都一样,| 表示左 | 位运算符:

PHP遍历文件夹并按筛选结果压缩下载_第1张图片

PHP遍历文件夹并按筛选结果压缩下载_第2张图片

这样以后,就可以正常下载了,或者根据文件是否存在进行三元判断是使用create还是overwrite也可以。

if($zip->open($destination,$overwrite ? ZIPARCHIVE::OVERWRITE : ZIPARCHIVE::CREATE) !== true) 
{
	return false;
}

 

你可能感兴趣的:(php)