*/ class FileHelper extends BaseFileHelper { public $_speed = 0; /** * 检测目录并循环创建目录 * * @param $catalogue */ public static function mkdirs($catalogue) { if (!file_exists($catalogue)) { self::mkdirs(dirname($catalogue)); mkdir($catalogue, 0777); exec('chown -R www.www '.$catalogue); } return true; } /** * 写入日志 * * @param $path * @param $content * @return bool|int */ public static function writeLog($path, $content) { $use_num = self::sysProbe(); // $use_num = Yii::$app->redis->get(CacheKeyEnum::SYSTEM_PROBE); if ($use_num > 98) { return false; } self::mkdirs(dirname($path)); return file_put_contents($path, date('Y-m-d H:i:s') . ">>>" . $content . "\r\n", FILE_APPEND); } /** * 检测磁盘空间 * @return bool|int|string */ public static function sysProbe() { $total = round(@disk_total_space(".") / (1024 * 1024 * 1024), 2); $free = round(@disk_free_space(".") / (1024 * 1024 * 1024), 2); $use_num = intval($total - $free) ? : 1; // exec("df -h", $systemInfo); // $disk = $systemInfo[5] ?? $systemInfo[1]; //没有找到其他磁盘就默认第一个 // $use_num = 1; // if ($disk) { // $use_num = trim(substr($disk, -10, -7)) ? : 1; //媒介保后端服务器磁盘用量 // } // Yii::$app->redis->setex(CacheKeyEnum::SYSTEM_PROBE, 3600, $use_num); return $use_num; } /** * 获取文件夹大小 * * @param string $dir 根文件夹路径 * @return int */ public static function getDirSize($dir) { $handle = opendir($dir); $sizeResult = 0; while (false !== ($FolderOrFile = readdir($handle))) { if ($FolderOrFile != "." && $FolderOrFile != "..") { if (is_dir("$dir/$FolderOrFile")) { $sizeResult += self::getDirSize("$dir/$FolderOrFile"); } else { $sizeResult += filesize("$dir/$FolderOrFile"); } } } closedir($handle); return $sizeResult; } /** * 基于数组创建目录 * * @param $files */ public static function createDirOrFiles($files) { foreach ($files as $key => $value) { if (substr($value, -1) == '/') { mkdir($value); } else { file_put_contents($value, ''); } } } /** * 文件大小字节转换对应的单位 * @param $size * @return string */ public static function convert($size) { $unit = array('b','kb','MB','GB','tb','pb'); return round($size / pow(1024, ($i = floor(log($size, 1024)))), 2) . '' . $unit[$i]; } public static function downloadVideo($url) { // $is_url = Url::isUrl($url); // if (!$is_url) { // exit('不是正确的链接地址'); //exit掉,下载下来打开会显示无法播放,格式不支持,文件已损坏等 // } //获取文件信息 // $fileExt = pathinfo($url); //获取文件的扩展名 // $allowDownExt = array ('mp4', 'mov'); //检测文件类型是否允许下载 // if (!in_array($fileExt['extension'], $allowDownExt)) { // exit('不支持该格式'); // } // 设置浏览器下载的文件名,这里还以原文件名一样 $filename = basename($url); // 获取远程文件大小 // 注意filesize()无法获取远程文件大小 $headers = get_headers($url, 1); $fileSize = $headers['Content-Length']; if (ini_get('zlib.output_compression')) { ini_set('zlib.output_compression', 'Off'); } header_remove('Content-Encoding'); // 设置header头 // 因为不知道文件是什么类型的,告诉浏览器输出的是字节流 header('Content-Type: application/octet-stream'); // 告诉浏览器返回的文件大小类型是字节 header('Accept-Ranges:bytes'); // 告诉浏览器返回的文件大小 header('Content-Length: ' . $fileSize); // 告诉浏览器文件作为附件处理并且设定最终下载完成的文件名称 header('Content-Disposition: attachment; filename="' . $filename . '"'); //针对大文件,规定每次读取文件的字节数为4096字节,直接输出数据 $read_buffer = 4096; //4096 $handle = fopen($url, 'rb'); //总的缓冲的字节数 $sum_buffer = 0; //只要没到文件尾,就一直读取 while (!feof($handle) && $sum_buffer < $fileSize) { echo fread($handle, $read_buffer); $sum_buffer += $read_buffer; } fclose($handle); exit; } /** * @param String $file 要下载的文件路径 * @param String $name 文件名称,为空则与下载的文件名称一样 * @param boolean $reload 是否开启断点续传 * @return string */ public static function downloadFile($file, $name = '', $reload = false) { $log_path = Yii::getAlias('@runtime') . '/api/' . date('Ym') . '/' . date('d') . '/download.txt'; FileHelper::writeLog($log_path, $file); $fp = fopen($file, 'rb'); if ($fp) { if ($name == '') { $name = basename($file); } $header_array = get_headers($file, true); // 下载本地文件,获取文件大小 if (!$header_array) { $file_size = filesize($file); } else { $file_size = $header_array['Content-Length']; } FileHelper::writeLog($log_path, json_encode($_SERVER, JSON_UNESCAPED_UNICODE)); if (isset($_SERVER['HTTP_RANGE']) && !empty($_SERVER['HTTP_RANGE'])) { $ranges = self::getRange($file_size); } else { //第一次连接 $size2 = $file_size - 1; header("Content-Range: bytes 0-$size2/$file_size"); //Content-Range: bytes 0-4988927/4988928 header("Content-Length: " . $file_size); //输出总长 } $ua = $_SERVER["HTTP_USER_AGENT"];//判断是什么类型浏览器 header('cache-control:public'); header('content-type:application/octet-stream'); $encoded_filename = urlencode($name); $encoded_filename = str_replace("+", "%20", $encoded_filename); //解决下载文件名乱码 if (preg_match("/MSIE/", $ua) || preg_match("/Trident/", $ua)) { header('Content-Disposition: attachment; filename="' . $encoded_filename . '"'); } else if (preg_match("/Firefox/", $ua)) { header('Content-Disposition: attachment; filename*="utf8\'\'' . $name . '"'); } else if (preg_match("/Chrome/", $ua)) { header('Content-Disposition: attachment; filename="' . $encoded_filename . '"'); } else { header('Content-Disposition: attachment; filename="' . $name . '"'); } //header('Content-Disposition: attachment; filename="' . $name . '"'); if ($reload && $ranges != null) { // 使用续传 header('HTTP/1.1 206 Partial Content'); header('Accept-Ranges:bytes'); // 剩余长度 header(sprintf('content-length:%u', $ranges['end'] - $ranges['start'])); // range信息 header(sprintf('content-range:bytes %s-%s/%s', $ranges['start'], $ranges['end'], $file_size)); FileHelper::writeLog($log_path, sprintf('content-length:%u', $ranges['end'] - $ranges['start'])); // fp指针跳到断点位置 fseek($fp, sprintf('%u', $ranges['start'])); } else { header('HTTP/1.1 200 OK'); header('content-length:' . $file_size); } while (!feof($fp)) { echo fread($fp, 4096); ob_flush(); } ($fp != null) && fclose($fp); } else { return ''; } } /** 设置下载速度 * @param int $speed */ public function setSpeed($speed) { if (is_numeric($speed) && $speed > 16 && $speed < 4096) { $this->_speed = $speed; } } /** 获取header range信息 * @param int $file_size 文件大小 * @return Array */ private static function getRange($file_size) { if (isset($_SERVER['HTTP_RANGE']) && !empty($_SERVER['HTTP_RANGE'])) { $range = $_SERVER['HTTP_RANGE']; $range = preg_replace('/[\s|,].*/', '', $range); $range = explode('-', substr($range, 6)); if (count($range) < 2) { $range[1] = $file_size; } $range = array_combine(array('start','end'), $range); if (empty($range['start'])) { $range['start'] = 0; } if (empty($range['end'])) { $range['end'] = $file_size; } return $range; } return null; } }