能否修改Nextcloud节点的filecache大小?应用开发遇监听问题
解决Nextcloud软链接目录空间占用翻倍问题
问题背景
开发Nextcloud应用时遇到软链接目录被重复计算空间的问题:实际仅占用一份存储,但Nextcloud的filecache将其大小计算为两倍。尝试使用废弃的Hooks方案无效,转而用EventListener监听FolderScannedEvent或NodeAddedToCacheEvent更新filecache条目,但监听器未执行,且缺乏调试与事件触发场景的文档参考。
现有代码
lib/AppInfo/Application.php
namespace OCA\Application\AppInfo; use OCA\Application\Event\RegistrationEventListener; use OCP\AppFramework\App; use OCP\EventDispatcher\IEventDispatcher; use OCP\Files\Events\FolderScannedEvent; class Application extends App { public const APP_ID = 'application'; public function __construct(IEventDispatcher $dispatcher) { parent::__construct(self::APP_ID); $dispatcher->addServiceListener(FolderScannedEvent::class, RegistrationEventListener::class, 1); } }
lib/Event/RegistrationEventListener.php
<?php namespace OCA\Application\Event; use OC\Files\Storage\Storage; use OCA\AdminAudit\AuditLogger; use OCP\EventDispatcher\Event; use OCP\EventDispatcher\IEventListener; use OCP\Files\Events\FolderScannedEvent; class RegistrationEventListener implements IEventListener { private $logger; private $storage; public function __construct(AuditLogger $logger, Storage $storage) { $this->logger = $logger; $this->storage = $storage; } /** * @inheritDoc */ public function handle(FolderScannedEvent|Event $event): void { if(is_link($event->getAbsolutePath())) { $fileId = $this->storage->getCache()->getId($event->getAbsolutePath()); $this->storage->getCache()->update($fileId, [ 'size' => 0 ]); } } }
问题排查与修复方案
1. 事件注册方式错误
Nextcloud当前推荐使用IBootstrap接口进行事件注册,而非直接在构造函数中注入IEventDispatcher,旧的注册方式可能导致监听器未被正确加载。
2. 监听器依赖注入错误
- 直接注入
Storage实例无效:Storage是与具体文件路径绑定的实例,无法通过DI容器全局注入,需从事件对象中获取对应存储。 - 使用
AuditLogger不合适:该组件用于审计操作,普通调试应使用Nextcloud标准的ILogger。
3. 事件处理逻辑缺陷
getAbsolutePath()返回服务器本地路径,多存储场景下可能不准确,应使用Nextcloud文件系统的Node对象获取路径与属性。- 直接用
is_link()判断软链接可能跨存储失效,应使用Nextcloud内置的FileInfo::TYPE_SYMLINK类型判断。
修正后的代码
lib/AppInfo/Application.php
namespace OCA\Application\AppInfo; use OCA\Application\Event\RegistrationEventListener; use OCP\AppFramework\App; use OCP\AppFramework\Bootstrap\IBootContext; use OCP\AppFramework\Bootstrap\IBootstrap; use OCP\AppFramework\Bootstrap\IRegistrationContext; use OCP\Files\Events\FolderScannedEvent; use OCP\Files\Events\NodeAddedToCacheEvent; class Application extends App implements IBootstrap { public const APP_ID = 'application'; public function __construct() { parent::__construct(self::APP_ID); } public function register(IRegistrationContext $context): void { // 注册文件夹扫描事件 $context->registerEventListener(FolderScannedEvent::class, RegistrationEventListener::class); // 注册文件/目录加入缓存事件(覆盖新添加的软链接场景) $context->registerEventListener(NodeAddedToCacheEvent::class, RegistrationEventListener::class); } public function boot(IBootContext $context): void { // 启动阶段无需额外操作 } }
lib/Event/RegistrationEventListener.php
<?php namespace OCA\Application\Event; use OCP\EventDispatcher\IEventListener; use OCP\Files\Events\FolderScannedEvent; use OCP\Files\Events\NodeAddedToCacheEvent; use OCP\ILogger; class RegistrationEventListener implements IEventListener { private $logger; public function __construct(ILogger $logger) { $this->logger = $logger; } public function handle(FolderScannedEvent|NodeAddedToCacheEvent $event): void { // 统一获取事件对应的Node对象 $node = method_exists($event, 'getNode') ? $event->getNode() : null; if (!$node) { $this->logger->warning('No node found in event', ['app' => 'application']); return; } $path = $node->getPath(); $this->logger->debug("Event triggered for path: {$path}", ['app' => 'application']); // 判断是否为软链接 if ($node->getType() === \OCP\Files\FileInfo::TYPE_SYMLINK) { $storage = $node->getStorage(); $cache = $storage->getCache(); $fileId = $cache->getId($path); if ($fileId) { $result = $cache->update($fileId, ['size' => 0]); if ($result) { $this->logger->debug("Successfully updated symlink {$path} size to 0", ['app' => 'application']); } else { $this->logger->error("Failed to update symlink {$path} size", ['app' => 'application']); } } else { $this->logger->warning("No filecache entry found for path: {$path}", ['app' => 'application']); } } } }
调试方法
1. 启用调试日志
修改config/config.php,添加/修改以下配置:
'debug' => true, 'log_level' => 0, // 0=Debug, 1=Info, 2=Warning, 3=Error 'logfile' => '/path/to/nextcloud.log', // 可选,指定日志路径
2. 触发事件
- 手动扫描文件夹:在Nextcloud后台进入「设置」→「管理」→「存储」,找到对应存储点击「扫描文件」。
- 命令行扫描:执行Nextcloud occ命令:
sudo -u www-data php occ files:scan --user <你的用户名>
3. 查看日志
打开data/nextcloud.log(或自定义路径),搜索application关键词,确认监听器是否触发、逻辑是否执行。
内容的提问来源于stack exchange,提问作者Atronix
相关产品推荐
相关产品推荐

