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

Php “标签上的问题”;“文件系统”;,php,language-agnostic,file,operating-system,filesystems,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) {

出于娱乐的原因,我编写了一个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;
    }
}

?>
Add('P:/xampplite/htdocs/tag/dunp3d-1.jpg','dunplogo');
$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+徽标+5f5174c498ffbfd9ae49975ddfa2f6eb.png
*/
回声';
打印($images->Get('*');
回声';
/*
[0]=>S:/+azul+branco+nuvem+4151ae7900f33788d0bba5fc6c29bee3.jpg
*/
回声';
打印($images->Get('azul nuvem'));
回声';
/*
[0]=>S:/+dunp+logo+0cedeb6f66cbfc3974c6b7ad86f4fbd3.jpg
[1] =>S:/+dunp+logo+8b9fcb119246bb6dcac1906ef964d565.jpg
[2] =>S:/+geoaki+徽标+5f5174c498ffbfd9ae49975ddfa2f6eb.png
*/
回声';
打印($images->Get('logo'));
回声';
/*
[0]=>S:/+dunp+logo+0cedeb6f66cbfc3974c6b7ad86f4fbd3.jpg
[1] =>S:/+dunp+logo+8b9fcb119246bb6dcac1906ef964d565.jpg
*/
回声';
打印($images->Get('logo dunp'));
回声';
/*
[0]=>S:/+geoaki+徽标+5f5174c498ffbfd9ae49975ddfa2f6eb.png
*/
回声';
打印($images->Get('geo*logo'));
回声';
?>
EDIT:由于有几条建议使用无服务器数据库或任何其他类型的查找表(XML、平面、键/值对等),我想澄清以下几点:虽然这段代码是用PHP编写的,但想法是将其移植到Python并用其制作桌面应用程序-这没有什么要做的(当然除了示例之外)使用PHP。此外,如果我必须使用某种查找表,我肯定会使用SQLite 3,但我要寻找的是一种解决方案,它不涉及除文件系统(文件夹、文件和硬链接)之外的任何其他“技术”。

你可能会说我疯了,但我在这里试图实现两个简单的目标:1)保持系统“无垃圾”(例如谁喜欢Thumbs.db或DS_STORE?)和2)如果出于某种原因查找表(在本例中是SQLite)变得繁忙、损坏、丢失或忘记(例如在备份中),则保持文件易于识别。


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

您应该将标记设置为目录,而不是文件名元素,即,而不是
/dir/tag1+tag2+tagN+MD5.EXT
/dir/tag1/tag2/tagN/MD5.EXT
。您将目录层次结构视为需要避免的事情,这在多个方面都是自食其果


如果因为您认为很难按需生成目录结构而采取这种避免措施,那么您应该研究第三个参数,
$recursive
,to.

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


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

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


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

如果您不想使用数据库,为什么不尝试使用xml,您可以这样列出所有数据:


MD5
tag5+tag4+tag3

您可以轻松地添加更多类似标题和描述的信息。

我会将这些信息插入数据库,即使它是一个轻量级的数据库,比如同一目录中的sqlite文件

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

  • P:/xamplite/htdocs/tag/geoaki.png.geoaki)
  • P:/xamplite/htdocs/tag/geoaki.png.logo)
这样做的优点是,您可以选择属于该文件的所有标记,例如

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

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

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

另一点需要注意的是,由于您希望搜索多个标记,因此需要确保文件名中的标记代码严格按照词汇顺序排列。然后,要立即搜索标记aa、f3和yz,请执行“
ls.*aa.*f3.*yz.
”,这将挑选出包含所有这些代码的文件名。

选择
<?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>';

?>
<file>
  <md5>MD5</md5>
  <body>tag5+tag4+tag3</body>
</file>
# 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');