标签“文件系统”上的问题

时间:2009-05-18 03:47:59

标签: php language-agnostic file operating-system filesystems

出于休闲的原因,我写了一个PHP类,用标签而不是分层方式对文件进行分类,标签以+ tag1 + tag2 + tagN + MD5.EXTENSION的形式存储在文件名中,因此我就是坚持FS / OS施加的字符限制(255)。这是班级:

<?php

class TagFS
{
    public $FS = null;

    function __construct($FS)
    {
        if (is_dir($FS) === true)
        {
            $this->FS = $this->Path($FS);
        }
    }

    function Add($path, $tag)
    {
        if (is_dir($path) === true)
        {
            $files = array_slice(scandir($path), 2);

            foreach ($files as $file)
            {
                $this->Add($this->Path($path) . $file, $tag);
            }

            return true;
        }

        else if (is_file($path) === true)
        {
            $file = md5_file($path);

            if (is_file($this->FS . $file) === false)
            {
                if (copy($path, $this->FS . $file) === false)
                {
                    return false;
                }
            }

            return $this->Link($this->FS . $file, $this->FS . '+' . $this->Tag($tag) . '+' . $file . '.' . strtolower(pathinfo($path, PATHINFO_EXTENSION)));
        }

        return false;
    }

    function Get($tag)
    {
        return glob($this->FS . '*+' . str_replace('+', '{+,+*+}', $this->Tag($tag)) . '+*', GLOB_BRACE);
    }

    function Link($source, $destination)
    {
        if (is_file($source) === true)
        {
            if (function_exists('link') === true)
            {
                return link($source, $destination);
            }

            if (is_file($destination) === false)
            {
                exec('fsutil hardlink create "' . $destination . '" "' . $source . '"');

                if (is_file($destination) === true)
                {
                    return true;
                }
            }
        }

        return false;
    }

    function Path($path)
    {
        if (file_exists($path) === true)
        {
            $path = str_replace('\\', '/', realpath($path));

            if ((is_dir($path) === true) && ($path[strlen($path) - 1] != '/'))
            {
                $path .= '/';
            }

            return $path;
        }

        return false;
    }

    function Tag($string)
    {
        /*
        TODO:
        Remove (on Windows):            . \ / : * ? " < > |
        Remove (on *nix):               . /
        Remove (on TagFS):              + * { }
        Remove (on TagFS - Possibly!)   -
        Max Chars (in Windows)          255
        Max Char (in *nix)              255
        */

        $result = array_filter(array_unique(explode(' ', $string)));

        if (empty($result) === false)
        {
            if (natcasesort($result) === true)
            {
                return strtolower(implode('+', $result));
            }
        }

        return false;
    }
}

?>

我相信这个系统适用于几个小标签,但我的问题是当整个文件名的大小超过255个字符时。我应该采取什么方法来绕过文件名限制?我正在考虑在同一文件的几个硬链接上拆分标签,但排列可能会导致系统崩溃。

还有其他方法可以解决这个问题吗?

编辑 - 一些使用示例:

<?php

$images = new TagFS('S:');

$images->Add('P:/xampplite/htdocs/tag/geoaki.png', 'geoaki logo');
$images->Add('P:/xampplite/htdocs/tag/cloud.jpg', 'geoaki cloud tag');
$images->Add('P:/xampplite/htdocs/tag/cloud.jpg', 'nuvem azul branco');
$images->Add('P:/xampplite/htdocs/tag/xml-full.gif', 'geoaki auto vin api service xml');
$images->Add('P:/xampplite/htdocs/tag/dunp3d-1.jpg', 'dunp logo');
$images->Add('P:/xampplite/htdocs/tag/d-proposta-04c.jpg', 'dunp logo');

/*
[0] => S:/+api+auto+geoaki+service+vin+xml+29be189cbc98fcb36a44d77acad13e18.gif
[1] => S:/+azul+branco+nuvem+4151ae7900f33788d0bba5fc6c29bee3.jpg
[2] => S:/+cloud+geoaki+tag+4151ae7900f33788d0bba5fc6c29bee3.jpg
[3] => S:/+dunp+logo+0cedeb6f66cbfc3974c6b7ad86f4fbd3.jpg
[4] => S:/+dunp+logo+8b9fcb119246bb6dcac1906ef964d565.jpg
[5] => S:/+geoaki+logo+5f5174c498ffbfd9ae49975ddfa2f6eb.png
*/
echo '<pre>';
print_r($images->Get('*'));
echo '</pre>';

/*
[0] => S:/+azul+branco+nuvem+4151ae7900f33788d0bba5fc6c29bee3.jpg
*/
echo '<pre>';
print_r($images->Get('azul nuvem'));
echo '</pre>';

/*
[0] => S:/+dunp+logo+0cedeb6f66cbfc3974c6b7ad86f4fbd3.jpg
[1] => S:/+dunp+logo+8b9fcb119246bb6dcac1906ef964d565.jpg
[2] => S:/+geoaki+logo+5f5174c498ffbfd9ae49975ddfa2f6eb.png
*/
echo '<pre>';
print_r($images->Get('logo'));
echo '</pre>';

/*
[0] => S:/+dunp+logo+0cedeb6f66cbfc3974c6b7ad86f4fbd3.jpg
[1] => S:/+dunp+logo+8b9fcb119246bb6dcac1906ef964d565.jpg
*/
echo '<pre>';
print_r($images->Get('logo dunp'));
echo '</pre>';

/*
[0] => S:/+geoaki+logo+5f5174c498ffbfd9ae49975ddfa2f6eb.png
*/
echo '<pre>';
print_r($images->Get('geo* logo'));
echo '</pre>';

?>

编辑:由于有几个建议使用无服务器数据库或任何其他类型的查找表(XML,平面,键/值对等),我想澄清以下内容:尽管此代码是用PHP的想法是将它移植到Python并从中创建一个桌面应用程序 - 这当然与PHP有关(当然除了示例)。此外,如果我必须使用某种查找表,我肯定会使用SQLite 3,但我正在寻找的是一种不涉及任何其他“技术”的解决方案文件系统(文件夹,文件和硬链接)。

你可能会叫我疯了,但我想在这里完成两个简单的目标:1)保持系统“垃圾”免费(例如谁喜欢Thumbs.db或DS_STORE?)和2)保留文件如果由于某种原因查找表(在本例中为SQLite)变得繁忙,损坏或丢失(例如在备份中),则容易识别。

PS:这应该在Linux,Mac和Windows上运行(在NTFS下)。

14 个答案:

答案 0 :(得分:16)

如果您使用的是硬/软链接,那么您可能会考虑为每个标记提供自己的目录,其中每个文件的链接都带有该“标记”。然后,当您获得多个标签时,您可以比较两者中找到的标签。然后这些文件可以存储在一个文件夹中,当然它们在名称上是唯一的。

我不知道这与标记命名的元文件有什么不同,然后列出该标记中存在的所有文件。

答案 1 :(得分:4)

您可能希望为您关注的每个文件夹创建一个标记缓存,类似于Windows在浏览文件夹时创建Thumbs.db文件以缓存拇指的方式。

创建这样的元数据文件具有跨多个不同文件系统工作的优势,而不会遇到文件名限制。

答案 2 :(得分:4)

我会将这些信息插入到数据库中,即使它是轻量级的,就像同一目录中的sqlite文件一样。

如果您不想这样做,您可以创建文件的硬链接而不进行任何排列。每个标签一个文件。使用geoaki和徽标标记P:/xampplite/htdocs/tag/geoaki.png会导致两个文件都是指向与原始文件相同数据的硬链接:

  • ,P:/xampplite/htdocs/tag/geoaki.png.geoaki)
  • ,P:/xampplite/htdocs/tag/geoaki.png.logo)

这样做的好处是,您可以使用glob()选择属于该文件的所有代码。

# All tags
$tags = array();
files = glob('P:/xampplite/htdocs/tag/geoaki.png.*')
foreach ($files as $file) {
    if (fileinode($file) === fileinode('P:/xampplite/htdocs/tag/geoaki.png')) {
        $tags[] = substr($file, strlen('P:/xampplite/htdocs/tag/geoaki.png.'));
    }
}

# Check if file has tag foo:
file_exists('P:/xampplite/htdocs/tag/geoaki.png.foo')
    && fileinode(P:/xampplite/htdocs/tag/geoaki.png.foo) === fileinode('P:/xampplite/htdocs/tag/geoaki.png');

还有一件事:仅依靠md5哈希来识别文件是不安全的,最好使用文件名作为标识符,这在文件夹中保证是唯一的。 md5作为标识符的负面影响是:

  • 一旦文件发生变化,系统就会中断
  • md5中存在冲突,两个不同的文件可能具有相同的md5哈希(概率很小,但存在)

答案 3 :(得分:3)

你已经充分缩小了这个问题,我相信答案是:“不。”

您不需要标记的中央注册表,因为它可能会损坏。

您不希望每个目录中隐藏的文件或文件都保存数据,因为这是“垃圾”。

你可能不希望有一组并行的目录或带有链接的目录,因为当你移动东西时它会过时并且可能在文件系统上构成“垃圾”。

肯定不希望将标记放在文件内容中。

那么除了目录结构中的文件名之外,还有其他地方可以放置标签吗?

没有。 (或者至少没有便携的东西)。

当然除了文件名或实际文件本身之外,没有任何地方可以保留元数据,这些文件将保留在文件中(使用常用工具复制和移动时),这些文件可以在所有三个主要操作系统上运行你提到(Linux,Mac,Win)。

如果有一个可移植的元数据系统可以做到这一点会很好,但事实并非如此。我的印象是,对于标记的最佳方式,没有达成一致意见。因此,每个系统都采用不同的方式,并采用不同的权衡方式。

我认为相对于操作系统中的大多数主要思想(分层文件系统,GUI界面等),使用标记是一个相对较新的想法。在所有三个系统中共享的大多数设施都是相当古老和既定的想法。

您最好的选择可能是研究每个系统如何做到这一点,然后编写一个可以在系统之间提供最低功能分支的库。

也许某人已经为Python编写了一个已经执行此操作的库?

C.J。

答案 4 :(得分:2)

更多的是头脑风暴而不是答案。

正如@CJ指出的那样,没有任何外部元数据,并且限制为255字节作为文件名标识加上'tag-cloud',你的tagfs仍然是个问题。

符号链接很好。不是将所有标记名打包成一个文件名,而是可以将标记分布在几个文件上,或者 - 为了空间 - 符号链接。步骤进行:

  1. 计算给定文件的校验和或哈希值
  2. 以某种格式存储符号链接,例如&lt; hash&gt; -tag或tag-&lt; hash&gt;
  3. 我明白,这就是'垃圾'的意思,但是如果你想在固定长度的字符串中存储任意数量的任意标签,你就会遇到信息障碍 迟早。使用数据库可以更好地扩展,但存储和检索符号链接应该 易于实施。 “垃圾”可以存储在单个元数据存储库中 一个领先的“点”,这是一些操作系统上广泛使用和建立的实践。

    祝你好运!

答案 5 :(得分:1)

实际上,我已经构建了这个实用程序的shell脚本实现,并将其与nautilus文件浏览器集成...

我使用了软链接方法:名为.tags的目录包含所有“标记”,标记只是.tags目录中的目录。

如果文件标记为“fun”,则会在.tags / fun中创建指向它的软链接。但是,此方法不适合按标签搜索。

如果您也想支持搜索,我建议您使用sqlite。

欢呼,jrh。

答案 6 :(得分:1)

文件系统是您的数据库,因此请使用它。

  1. 为您的文件提供“唯一名称”。无论文件名是什么,只要它在整个空间中是唯一的。文件名与标签无关。

  2. 将文件名散列到“storage”目录。如果您没有bazillion文件(&lt; 1000-2000),则可以将所有文件存储在一个目录中。否则,制作一堆“bucket”目录,并将文件散列到正确的目录。显然,这个过程基于文件名确定。

  3. 对于文件中的每个标记,要么在“tag”目录中存储同名的“空”文件,要么只有一个“标记文件”列出该标记中的文件。同样,如果您希望在特定标记中包含数以万计的文件,请将文件散列到存储桶中。

  4. 要将标记添加到文件,只需将文件引用添加到正确的标记dir即可。删除标签,同样的事情。

    要删除文件,只需从主存储中删除该文件即可。迭代标记引用时,可以检查该点的文件并懒惰地删除条目。无论如何,你可能会在文件中找到任何有趣的内容。

    如果要存储文件的实际元数据,请创建镜像“元数据”目录。添加文件时,将其放在文件存储目录中,并使用相同的方案将匹配的元数据文件放在“元数据存储”目录中。通过删除原始文件及其元数据来删除文件。

    只是简单的文件操作,没有文件系统恶作剧(超出散列目录存储桶),没有链接,属性,你有什么。

    这为每个文件提供了“无限制”标签,您可以从命令行或文件浏览器管理它,只需要Mark I Eyeball所需的工具。您还可以获得对实际文件的永久引用(因为它的名称永远不会更改)。

    最黑暗的部分是你需要“扫描标签云”以找出文件的标签。如果您选择使用元数据文件,则可以在其中维护标记列表(这会使标记/取消标记操作复杂化,但不会非常糟糕)。

答案 7 :(得分:1)

如果您的操作系统和文件系统支持文件扩展属性,请使用它来存储标记。在OS X和FreeBSD上,请参阅setxattr和getxattr手册页; Linux和Solaris具有类似的功能。 Windows支持NTFS中的扩展属性。有关详细信息,请参阅维基百科上的“扩展文件属性”。

答案 8 :(得分:0)

您应该创建代码目录而不是文件名元素,即代替/dir/tag1+tag2+tagN+MD5.EXT/dir/tag1/tag2/tagN/MD5.EXT。通过将目录层次结构视为可以避免的事情,您可以通过多种方式自我介入。

如果您因为认为很难按需生成目录结构而参与此避免,那么您应该查看第三个参数$recursivePHP's mkdir

答案 9 :(得分:0)

选择避免使用SQLite,因为它“不是PHP本机”似乎是一种错误的二分法,因为它几乎被编译到PHP的每个实际发行版中。如果你想要一个非SQL解决方案,berkeleydb提供了一个简单的键值存储,你可以用来将文件列表与任何给定的标签文件名与标签列表相关联。

但是请使用SQL解决方案。它比你想象的快,便携,简单。

答案 10 :(得分:0)

“我应采取什么方法来绕过文件名限制?”

支持标签的文件系统怎么样? Tagsistant您未指定操作系统。

答案 11 :(得分:0)

在Windows中,您可以右键单击文件&gt;属性并添加注释和其他数据。您可以将它用于您的标记系统(当然,用户可以非常容易地使用它,这是可以的)

问题是,其他系统是否可以从Windows文件属性中读取这些注释和数据?

答案 12 :(得分:-1)

如果你不想使用数据库,为什么不试试xml,你可以列出你的所有数据:

<file>
  <md5>MD5</md5>
  <body>tag5+tag4+tag3</body>
</file>

您可以轻松添加标题和说明等内容。

答案 13 :(得分:-1)

标签的整个要点是能够快速搜索多个标签组合。理想情况下,您希望拥有一个带有标记表{tag,path-to-file}的数据库。如果您已将标记保留在文件名中,则需要使用某种压缩。保留一个查找表(db或flat文件),将每个标签映射到一个2字符的代码(例如aa:tag1,ab:tag2,ac:tag3 ......)。坚持ascii,这应该给你~10k标签,如果这还不够用三个字符。现在您的文件名将类似于aa.ag.f2.gx.ty.extension

需要注意的另一点是,由于您要搜索多个标记,因此您需要确保文件名中的标记代码符合严格的词法顺序。然后,要一次搜索标签aa,f3和yz,请执行“ls .*aa.*f3.*yz.*”,这将选择包含所有这些代码的文件名。