我有一个网络存储设备,其中包含由[artist]/[album]
层次结构组织的数十万个mp3文件。我需要按需编程识别新添加的艺术家文件夹和/或新添加的相册文件夹(不是监控,而是按要求)。
我们的开发服务器是基于Windows的,生产服务器将是FreeBSD。跨平台解决方案是最佳的,因为生产服务器可能并不总是* nix,并且我希望花费尽可能少的时间来协调开发服务器和生产服务器之间的(不可避免的)差异。
我有一个与Windows平台相关的工作概念验证:使用Scripting.FileSystemObject
COM对象我遍历所有顶级(艺术家)目录并检查目录的大小。如果有更改,则会进一步探索该目录以查找新的相册文件夹。当迭代目录时,路径和文件大小被收集到一个数组中,我下次将其序列化为一个文件。此数组用于后续调用,既可以识别已更改的艺术家目录(添加新专辑),也可以识别全新的艺术家目录。
这感觉很复杂,正如我所提到的那样,它与平台有关。 要把它煮沸,我的目标是:
执行时间不是问题,安全性不是障碍:这是一个仅使用内部网资产的内部项目,因此我们可以做任何事情来促进所需的最终结果。
这是我的工作概念验证:
// read the cached list of artist folders
$folder_list_cache_file = 'seartistfolderlist.pctf';
$fh = fopen($folder_list_cache_file, 'r');
$folder_list_cache = fread($fh, filesize($folder_list_cache_file));
fclose($fh);
if (!$folder_list_cache)
$folder_list_cache = '';
$folder_list_cache = unserialize($folder_list_cache);
if (!is_array($folder_list_cache))
$folder_list_cache = array();
// container arrays
$found_artist_folders = array();
$newly_found_artist_folders = array();
$changed_artist_folders = array();
$filesystem = new COM('Scripting.FileSystemObject');
$dir = "//network_path_to_folders/";
if ($handle = opendir($dir)) {
// loop the directories
while (false !== ($file = readdir($handle))) {
// skip non-entities
if ($file == '.' || $file == '..')
continue;
// make a key-friendly version of the artist name, skip invalids
// ie 10000-maniacs
$file_t = trim(post_slug($file));
if (strlen($file_t) < 1)
continue;
// build the full path
$pth = $dir.$file;
// skip loose top-level files
if (!is_dir($pth))
continue;
// attempt to get the size of the directory
$size = 'ERR';
try {
$f = $filesystem->getfolder($pth);
$size = $f->Size();
} catch (Exception $e) {
/* failed to get size */
}
// if the artist is not known, they are newly added
if (!array_key_exists($file_t, $folder_list_cache)) {
$newly_found_artist_folders[$file_t] = $file;
} elseif (array_key_exists($file_t, $folder_list_cache) && $size != $folder_list_cache[$file_t]['size']) {
// if the artist is known but the size is different, a new album is added
$changed_artist_folders[] = $file;
}
// build a list of everything, along with file size to write into the cache file
$found_artist_folders[$file_t] = array (
'path'=>$file,
'size'=>$size
);
}
closedir($handle);
}
// write the list to a file for next time
$fh = fopen($folder_list_cache_file, 'w') or die("can't open file");
fwrite($fh, serialize($found_artist_folders));
fclose($fh);
// deal with discovered additions and changes....
另外要提的是:因为这些是MP3,我正在处理的尺寸很大。事实上,我必须注意PHP对未经过整数的限制。该驱动器目前的利用率为90%,为1.7TB(是的,RAID中的SATA),一组新的多TB驱动器很快就会被添加,只能在短时间内完成。
修改
我没有提到数据库,因为我认为这将是一个不必要的细节,但有一个数据库。这个脚本正在寻找我们音乐库数字部分的新增内容;在代码的末尾,它表示“处理已发现的添加和更改”,它正在读取ID3标签并执行Amazon查找,然后将新内容添加到数据库表中。有人会来查看新增内容并筛选数据,然后将其添加到可供播放的专辑“官方”数据库中。我们正在处理的许多歌曲都是由当地艺术家处理的,因此ID3和亚马逊的查询不会提供曲目标题,专辑名称等。在这种情况下,人工干预对于填写缺失的数据至关重要。 / p>
答案 0 :(得分:2)
BSD端最简单的事情是find
脚本,它只查找ctime大于上次运行的inode。
将一个sentinel文件留在某个地方“存储”最后一次运行时间,你可以用一个简单的
touch /tmp/find_sentinel
然后
find /top/of/mp3/tree --cnewer /tmp/find_sentinel
将生成自触摸find_sentinel文件以来已更改的文件/目录列表。通过cron运行它将获得定期更新,执行查找的脚本可以将返回的文件数据消化到数据库中进行处理。
您可以使用Cygwin在Windows端完成类似的操作,这会提供相同的“查找”应用。
答案 1 :(得分:1)
DirectoryIterator
将帮助您浏览文件系统。您应该考虑将信息放在数据库中。
答案 2 :(得分:0)
我会使用一个解决方案来枚举MySQL数据库中每个文件夹的内容;您的扫描可以快速检查数据库中列出的内容,并添加尚不存在的条目。这为您提供了很好的枚举和内容的可搜索性,并且应该能够快速满足您的需求。