You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

能否修改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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.15 02:00:56