出于休闲的原因,我写了一个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下)。
答案 0 :(得分:16)
如果您使用的是硬/软链接,那么您可能会考虑为每个标记提供自己的目录,其中每个文件的链接都带有该“标记”。然后,当您获得多个标签时,您可以比较两者中找到的标签。然后这些文件可以存储在一个文件夹中,当然它们在名称上是唯一的。
我不知道这与标记命名的元文件有什么不同,然后列出该标记中存在的所有文件。
答案 1 :(得分:4)
您可能希望为您关注的每个文件夹创建一个标记缓存,类似于Windows在浏览文件夹时创建Thumbs.db文件以缓存拇指的方式。
创建这样的元数据文件具有跨多个不同文件系统工作的优势,而不会遇到文件名限制。
答案 2 :(得分:4)
我会将这些信息插入到数据库中,即使它是轻量级的,就像同一目录中的sqlite文件一样。
如果您不想这样做,您可以创建文件的硬链接而不进行任何排列。每个标签一个文件。使用geoaki和徽标标记P:/xampplite/htdocs/tag/geoaki.png会导致两个文件都是指向与原始文件相同数据的硬链接:
这样做的好处是,您可以使用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作为标识符的负面影响是:
答案 3 :(得分:3)
你已经充分缩小了这个问题,我相信答案是:“不。”
您不需要标记的中央注册表,因为它可能会损坏。
您不希望每个目录中隐藏的文件或文件都保存数据,因为这是“垃圾”。
你可能不希望有一组并行的目录或带有链接的目录,因为当你移动东西时它会过时并且可能在文件系统上构成“垃圾”。
您肯定不希望将标记放在文件内容中。
那么除了目录结构中的文件名之外,还有其他地方可以放置标签吗?
没有。 (或者至少没有便携的东西)。
当然除了文件名或实际文件本身之外,没有任何地方可以保留元数据,这些文件将保留在文件中(使用常用工具复制和移动时),这些文件可以在所有三个主要操作系统上运行你提到(Linux,Mac,Win)。
如果有一个可移植的元数据系统可以做到这一点会很好,但事实并非如此。我的印象是,对于标记的最佳方式,没有达成一致意见。因此,每个系统都采用不同的方式,并采用不同的权衡方式。
我认为相对于操作系统中的大多数主要思想(分层文件系统,GUI界面等),使用标记是一个相对较新的想法。在所有三个系统中共享的大多数设施都是相当古老和既定的想法。
您最好的选择可能是研究每个系统如何做到这一点,然后编写一个可以在系统之间提供最低功能分支的库。
也许某人已经为Python编写了一个已经执行此操作的库?
C.J。
答案 4 :(得分:2)
更多的是头脑风暴而不是答案。
正如@CJ指出的那样,没有任何外部元数据,并且限制为255字节作为文件名标识加上'tag-cloud',你的tagfs仍然是个问题。
符号链接很好。不是将所有标记名打包成一个文件名,而是可以将标记分布在几个文件上,或者 - 为了空间 - 符号链接。步骤进行:
我明白,这就是'垃圾'的意思,但是如果你想在固定长度的字符串中存储任意数量的任意标签,你就会遇到信息障碍 迟早。使用数据库可以更好地扩展,但存储和检索符号链接应该 易于实施。 “垃圾”可以存储在单个元数据存储库中 一个领先的“点”,这是一些操作系统上广泛使用和建立的实践。
祝你好运!答案 5 :(得分:1)
实际上,我已经构建了这个实用程序的shell脚本实现,并将其与nautilus文件浏览器集成...
我使用了软链接方法:名为.tags的目录包含所有“标记”,标记只是.tags目录中的目录。
如果文件标记为“fun”,则会在.tags / fun中创建指向它的软链接。但是,此方法不适合按标签搜索。
如果您也想支持搜索,我建议您使用sqlite。
欢呼,jrh。答案 6 :(得分:1)
文件系统是您的数据库,因此请使用它。
为您的文件提供“唯一名称”。无论文件名是什么,只要它在整个空间中是唯一的。文件名与标签无关。
将文件名散列到“storage”目录。如果您没有bazillion文件(&lt; 1000-2000),则可以将所有文件存储在一个目录中。否则,制作一堆“bucket”目录,并将文件散列到正确的目录。显然,这个过程基于文件名确定。
对于文件中的每个标记,要么在“tag”目录中存储同名的“空”文件,要么只有一个“标记文件”列出该标记中的文件。同样,如果您希望在特定标记中包含数以万计的文件,请将文件散列到存储桶中。
要将标记添加到文件,只需将文件引用添加到正确的标记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
。通过将目录层次结构视为可以避免的事情,您可以通过多种方式自我介入。
如果您因为认为很难按需生成目录结构而参与此避免,那么您应该查看第三个参数$recursive
到PHP'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.*
”,这将选择包含所有这些代码的文件名。