Browse Source

improve dir scan performance

master
filesite 5 months ago
parent
commit
2948a223e8
  1. 13
      controller/Controller.php
  2. 31
      lib/DirScanner.php
  3. 51
      themes/beauty/controller/ListController.php
  4. 65
      themes/beauty/controller/SiteController.php
  5. 38
      themes/beauty/views/site/index.php
  6. 24
      www/js/beauty.js

13
controller/Controller.php

@ -311,4 +311,17 @@ Class Controller { @@ -311,4 +311,17 @@ Class Controller {
return "{$prefix}_{$dataType}_{$maxScanDeep}_{$cateId}";
}
protected function getCurrentWebroot($realpath) {
$realpath = preg_replace('/\/[^\/]+\.[^\/]+$/', '', $realpath); //删除文件名,只保留目录
if (empty($realpath)) {return '/';}
$webroot = FSC::$app['config']['content_directory'];
$arr = explode($webroot, $realpath);
if (count($arr) < 2) {
return $webroot;
}
return "{$webroot}{$arr[1]}/";
}
}

31
lib/DirScanner.php

@ -144,7 +144,9 @@ Class DirScanner { @@ -144,7 +144,9 @@ Class DirScanner {
//根据文件路径生成唯一编号
public function getId($realpath) {
return !empty($realpath) ? md5($realpath) : '';
if ($realpath == '/') {return md5($realpath);}
return !empty($realpath) ? md5(preg_replace('/\/$/', '', $realpath)) : '';
}
//判断Nginx防盗链MD5加密方式字符串是否合格
@ -772,7 +774,7 @@ Class DirScanner { @@ -772,7 +774,7 @@ Class DirScanner {
}
}
}
}else if (!empty($dirid) && !empty($this->scanResults)) {
}else if (!empty($dirid) && !empty($this->scanResults) && !empty($this->scanResults[$dirid])) {
$directory = $this->scanResults[$dirid];
if (!empty($directory) && !empty($directory['files'])) {
foreach($directory['files'] as $id => $file) {
@ -794,4 +796,29 @@ Class DirScanner { @@ -794,4 +796,29 @@ Class DirScanner {
return $readme;
}
//获取目录下第一个图片作为封面图返回
public function getSnapshotImage($realpath, $imgExts = array('jpg', 'jpeg', 'png', 'webp', 'gif')) {
$iterator = new RecursiveIteratorIterator(
new RecursiveDirectoryIterator($realpath, RecursiveDirectoryIterator::SKIP_DOTS),
RecursiveIteratorIterator::SELF_FIRST
);
$imgUrl = '';
foreach ($iterator as $item) {
if ($item->isFile()) {
$extension = $item->getExtension();
if (in_array(strtolower($extension), $imgExts)) {
$imgPath = $item->getPath() . '/' . $item->getFilename();
$imgData = $this->getFileData($imgPath);
if (!empty($imgData['path'])) {
$imgUrl = $imgData['path'];
}
break;
}
}
}
return $imgUrl;
}
}

51
themes/beauty/controller/ListController.php

@ -36,11 +36,11 @@ Class ListController extends Controller { @@ -36,11 +36,11 @@ Class ListController extends Controller {
throw new Exception("缓存数据中找不到当前目录,请返回上一页重新进入!", 404);
}
$scanner->setWebRoot($this->getCurrentWebroot($currentDir));
$scanner->setWebRoot($this->getCurrentWebroot($currentDir['realpath']));
$scanner->setRootDir($currentDir['realpath']);
//优先从缓存读取数据
$maxScanDeep = 2; //最大扫描目录级数
$maxScanDeep = 0; //最大扫描目录级数
$cacheKey = $this->getCacheKey($cateId, 'tree', $maxScanDeep);
$cachedData = Common::getCacheFromFile($cacheKey);
if (!empty($cachedData)) {
@ -63,20 +63,31 @@ Class ListController extends Controller { @@ -63,20 +63,31 @@ Class ListController extends Controller {
}
//按照scanResults格式把当前目录扫描结果中的目录数据拼接到当前目录数据里: currentDir
if (!empty($currentDir['directories'])) {
foreach ($currentDir['directories'] as $id => $item) {
if (empty($scanResults[$id]['directories'])) {
continue;
if (!empty($scanResults)) {
$dirs = array();
$files = array();
foreach ($scanResults as $id => $item) {
if (!empty($item['directory'])) {
array_push($dirs, $item);
}else {
array_push($files, $item);
}
}
if (!empty($dirs)) {
$currentDir['directories'] = $dirs;
}
$currentDir['directories'][$id] = $scanResults[$id];
if (!empty($files)) {
$currentDir['files'] = $files;
}
$scanResults = array($cateId => $currentDir); //重新组装数据
}
$scanResults = array($cateId => $currentDir); //重新组装数据
//非首页统一从缓存获取目录数据,有效期 1 小时
$cacheKey = $this->getCacheKey('all', 'menu', 2);
$cacheKey = $this->getCacheKey('all', 'menu', $maxScanDeep);
$menus = Common::getCacheFromFile($cacheKey, 3600);
@ -94,11 +105,10 @@ Class ListController extends Controller { @@ -94,11 +105,10 @@ Class ListController extends Controller {
}
//获取默认mp3文件
$rootCateId = $this->get('id', '');
$mp3File = $scanner->getDefaultFile('mp3', $rootCateId);
if (empty($mp3File)) {
$mp3File = $scanner->getDefaultFile('mp3');
}
//优先从缓存获取默认mp3文件
$cacheKey = $this->getCacheKey('root', 'mp3', $maxScanDeep);
$mp3File = Common::getCacheFromFile($cacheKey);
//翻页支持
$page = $this->get('page', 1);
@ -152,16 +162,6 @@ Class ListController extends Controller { @@ -152,16 +162,6 @@ Class ListController extends Controller {
return $parent;
}
protected function getCurrentWebroot($currentDir) {
$webroot = FSC::$app['config']['content_directory'];
$arr = explode($webroot, $currentDir['realpath']);
if (count($arr) < 2) {
return $webroot;
}
return "{$webroot}{$arr[1]}/";
}
//根据目录结构以及当前目录获取面包屑
//缓存key统一生成,方便按规则获取上一级目录的缓存cid
protected function getBreadcrumbs($currentDir, $scanResults, $scanner) {
@ -183,7 +183,8 @@ Class ListController extends Controller { @@ -183,7 +183,8 @@ Class ListController extends Controller {
//下一级子目录的id
$parentCateId = $scanner->getId($parentCate);
$cacheKey = $this->getCacheKey($parentCateId, 'data');
$maxScanDeep = 0; //所有页面扫描深度都为 1
$cacheKey = $this->getCacheKey($parentCateId, 'data', $maxScanDeep);
array_push($breads, [
'id' => $cateId,

65
themes/beauty/controller/SiteController.php

@ -23,7 +23,7 @@ Class SiteController extends Controller { @@ -23,7 +23,7 @@ Class SiteController extends Controller {
//优先从缓存读取数据
$defaultCateId = $scanner->getId(preg_replace("/\/$/", '', realpath($rootDir)));
$maxScanDeep = 2; //最大扫描目录级数
$maxScanDeep = 0; //最大扫描目录级数
$cacheKey = $this->getCacheKey($defaultCateId, 'tree', $maxScanDeep);
$cachedData = Common::getCacheFromFile($cacheKey);
if (!empty($cachedData)) {
@ -45,15 +45,22 @@ Class SiteController extends Controller { @@ -45,15 +45,22 @@ Class SiteController extends Controller {
Common::saveCacheToFile($cacheKey, $scanResults);
}
//优先从缓存获取目录数据
$cacheKey = $this->getCacheKey('all', 'menu', $maxScanDeep);
$menus = Common::getCacheFromFile($cacheKey);
if (empty($menus)) {
if (empty($menus) && !empty($scanResults)) {
//获取目录
$menus = $scanner->getMenus();
//在path网址中追加cid缓存key参数
if (!empty($menus)) {
foreach ($menus as $index => $menu) {
$menus[$index]['cid'] = $cacheDataId;
$menus[$index]['path'] .= "&cid={$cacheDataId}";
}
}
$titles = array();
$readmeFile = $scanner->getDefaultReadme();
if (!empty($readmeFile)) {
@ -80,12 +87,8 @@ Class SiteController extends Controller { @@ -80,12 +87,8 @@ Class SiteController extends Controller {
}
//当前目录数据
$cateId = $this->get('id', $menus[0]['id']);
$subcate = $scanResults[$cateId];
//获取当前目录下的readme
$cateReadmeFile = $scanner->getDefaultReadme($cateId);
$cateReadmeFile = $scanner->getDefaultReadme($defaultCateId);
if (!empty($cateReadmeFile)) {
$Parsedown = new Parsedown();
$content = file_get_contents($cateReadmeFile['realpath']);
@ -93,11 +96,14 @@ Class SiteController extends Controller { @@ -93,11 +96,14 @@ Class SiteController extends Controller {
$htmlCateReadme = $scanner->fixMDUrls($cateReadmeFile['realpath'], $htmlCateReadme);
}
//获取默认mp3文件
$rootCateId = $this->get('id', '');
$mp3File = $scanner->getDefaultFile('mp3', $rootCateId);
//优先从缓存获取默认mp3文件
$cacheKey = $this->getCacheKey('root', 'mp3', $maxScanDeep);
$mp3File = Common::getCacheFromFile($cacheKey);
if (empty($mp3File)) {
$mp3File = $scanner->getDefaultFile('mp3');
$mp3File = $scanner->getDefaultFile('mp3', $defaultCateId);
if (!empty($mp3File)) {
Common::saveCacheToFile($cacheKey, $mp3File);
}
}
@ -114,13 +120,13 @@ Class SiteController extends Controller { @@ -114,13 +120,13 @@ Class SiteController extends Controller {
}
$viewName = 'index';
$params = compact(
'cateId', 'page', 'pageSize', 'cacheDataId',
'page', 'pageSize', 'cacheDataId',
'dirTree', 'scanResults', 'menus', 'htmlReadme', 'htmlCateReadme', 'mp3File'
);
return $this->render($viewName, $params, $pageTitle);
}
//清空缓存
//TODO: 清空所有缓存
public function actionCleancache() {
$prefix = FSC::$app['config']['theme'];
$cacheKey = "{$prefix}_allFilesTree";
@ -134,4 +140,35 @@ Class SiteController extends Controller { @@ -134,4 +140,35 @@ Class SiteController extends Controller {
return $this->renderJson(compact('code', 'msg'));
}
//根据目录id,获取第一张图网址作为封面图返回
public function actionDirsnap() {
$code = 1;
$msg = 'OK';
$url = '';
$cacheId = $this->post('cid', '');
$cateId = $this->post('id', '');
if (empty($cacheId) || empty($cateId)) {
$code = 0;
$msg = '参数不能为空';
}else {
//从缓存数据中获取目录的realpath
$cachedData = Common::getCacheFromFile($cacheId);
if (!empty($cachedData)) {
$realpath = $cachedData[$cateId]['realpath'];
$scanner = new DirScanner();
$scanner->setWebRoot($this->getCurrentWebroot($realpath));
$scanner->setRootDir($realpath);
$imgExts = !empty(FSC::$app['config']['supportedImageExts']) ? FSC::$app['config']['supportedImageExts'] : array('jpg', 'jpeg', 'png', 'webp', 'gif');
$url = $scanner->getSnapshotImage($realpath, $imgExts);
}else {
$code = 0;
$msg = '缓存数据已失效,请刷新网页';
}
}
return $this->renderJson(compact('code', 'msg', 'url'));
}
}

38
themes/beauty/views/site/index.php

@ -34,13 +34,13 @@ @@ -34,13 +34,13 @@
<ul class="nav navbar-fixed-left">
<?php
$selectedId = $viewData['cateId'];
$selectedId = !empty($viewData['cateId']) ? $viewData['cateId'] : '';
$breadcrumbs = !empty($viewData['breadcrumbs']) ? $viewData['breadcrumbs'] : [];
if (!empty($viewData['menus'])) { //只显示第一级目录
foreach ($viewData['menus'] as $index => $item) {
$selected = $item['id'] == $selectedId || (!empty($breadcrumbs) && $item['id'] == $breadcrumbs[0]['id']) ? 'active' : '';
echo <<<eof
<li class="{$selected}"><a href="/?id={$item['id']}">{$item['directory']}</a></li>
<li class="{$selected}"><a href="{$item['path']}">{$item['directory']}</a></li>
eof;
}
}
@ -84,6 +84,29 @@ eof; @@ -84,6 +84,29 @@ eof;
$category = !empty($viewData['scanResults'][$selectedId]) ? $viewData['scanResults'][$selectedId] : [];
$total = 0; //翻页支持
//如果没有选中任何目录,则把所有目录显示出来
if (empty($category) && !empty($viewData['menus'])) {
foreach ($viewData['menus'] as $index => $dir) {
echo <<<eof
<div class="im_item bor_radius col-xs-6 col-sm-4 col-md-3 col-lg-2">
<a href="{$dir['path']}" class="bor_radius dir_item" data-id="{$dir['id']}" data-cid="{$dir['cid']}">
eof;
$title = !empty($dir['title']) ? $dir['title'] : $dir['directory'];
echo <<<eof
<div class="im_img_title">
<span>
<img src="/img/beauty/folder.svg" alt="folder" width="20">
{$title}
</span>
</div>
</a>
</div>
eof;
}
}
//当前目录的描述介绍
if (!empty($category['description'])) {
echo <<<eof
@ -99,12 +122,11 @@ eof; @@ -99,12 +122,11 @@ eof;
}
if (!empty($category['directories'])) { //两级目录支持
$total = count($category['directories']); //翻页支持
$index = 0;
foreach ($category['directories'] as $dir) {
echo <<<eof
<div class="im_item bor_radius col-xs-6 col-sm-4 col-md-3 col-lg-2">
<a href="{$dir['path']}&cid={$viewData['cacheDataId']}" class="bor_radius">
<a href="{$dir['path']}&cid={$viewData['cacheDataId']}" class="bor_radius dir_item" data-id="{$dir['id']}" data-cid="{$viewData['cacheDataId']}">
eof;
if (!empty($dir['snapshot'])) {
@ -173,7 +195,7 @@ eof; @@ -173,7 +195,7 @@ eof;
$pageStartIndex = ($viewData['page']-1) * $viewData['pageSize'];
$index = 0;
foreach ($category['files'] as $file) {
if (!in_array($file['extension'], $imgExts)) {
if (empty($file['extension']) || !in_array($file['extension'], $imgExts)) {
continue;
}
@ -231,7 +253,9 @@ eof; @@ -231,7 +253,9 @@ eof;
<div class="text-center">
<?php
$pagination = Html::getPaginationHtmlCode($viewData['page'], $viewData['pageSize'], $total);
echo $pagination;
if ($total > 0) {
$pagination = Html::getPaginationHtmlCode($viewData['page'], $viewData['pageSize'], $total);
echo $pagination;
}
?>
</div>

24
www/js/beauty.js

@ -109,6 +109,30 @@ if ($('#image_site').get(0)) { @@ -109,6 +109,30 @@ if ($('#image_site').get(0)) {
saveLanpnumToLocalstorage(lanpnum);
});
//异步加载目录的封面图
$('.dir_item').each(function(index, el) {
var cid = $(el).attr('data-cid'), id = $(el).attr('data-id');
if ($(el).find('.im_img').length == 0) {
$.ajax({
url: '/site/dirsnap',
method: 'POST',
dataType: 'json',
data: {
cid: cid,
id: id
}
}).done(function(data) {
if (data.code == 1 && data.url) {
$(el).find('.im_img_title').before('<img src="' + data.url + '" class="bor_radius im_img">');
}else {
console.error('目录封面图获取失败:%s', data.msg);
}
}).fail(function(jqXHR, textStatus, errorThrown) {
console.error('获取封面图失败,错误信息:' + errorThrown);
});
}
});
//刷新缓存
$('.cleanCacheJS').click(function () {
$.ajax({

Loading…
Cancel
Save