CD Beaver -光盘介质存储管理






4.75/5 (4投票s)
用于归档和管理光盘介质的解决方案

引言
CD-Beaver 是一个用于编目光盘介质(如 CD 和 DVD)的简单程序。该应用程序使用 XML 作为其后端数据存储,并且是光盘收纳盒的绝佳搭档。CD Beaver 的目的是简化存储和查找光盘及其包含文件的过程。在本文中,我将演示 CD Beaver 的各种用途以及它如何使媒体存档更加轻松和减轻压力。
背景
与我大多数的个人项目一样,这个项目源于极度的沮丧。几年前,我的一位拥有小型企业的朋友正在寻找一种自动化的光盘存储系统。他很快就发现了 Opdicom 公司的一款名为 Disk Stakka 的解决方案。下面您可以看到一个类似他的设置;五个单元堆叠在一起,以 625 美元(每个单元 125 美元)的合理价格提供了 500 张光盘的存储解决方案。

经过几个月的试用,他又购买了 3 个单元,并坚持认为这是一项值得付出的解决方案。在他的推荐下,我决定自己也投资购买 4 个单元;当时我真的无法证明这笔开销是合理的。将所有光盘放入其中并编目所有文件,这需要大量的初始工作。起初,我非常满意地将我所有的 DVD 电影、视频游戏、备份光盘、电视录像、音乐和家庭照片安全地存储在这些单元中。
几个月之内,其中一个单元开始出现故障,一年之内所有的单元都出现了故障。光盘经常卡在单元里,最终从任何一个单元中取出光盘的唯一方法就是拆开它们,然后引导光盘沿其路径安全弹出系统。这造成了极大的尴尬,因为这总是在我招待客人时发生,或者在我需要访问光盘以应对紧急工作时发生。
对我来说,最初令人兴奋的旧问题解决方案变成了一场噩梦,我的媒体被劫持了,而要访问我的媒体的唯一方法就是闯入这个呆板的、吃光盘的野兽!几个月前,我决定这个极其糟糕的产品已无可救药,于是我着手创建一个替代方案。
设计过程
项目的目标非常简单。我需要一个存储和检索光盘介质的解决方案。
第一个解决方案是使用一个基本的电子表格,如下所示

对于 DVD 电影来说,电子表格解决方案是有效的,因为文件内容(AUDIO_TS/VIDEO_TS)不那么重要,但对于数据光盘来说,它会带来问题。让我们看一下下面的工作流程

从上面的工作流程可以看出,我有一个名为 Big Bertha 的 CD/DVD 携带盒。在 20C 槽中有一个名为 PBS Frontline S2004 的光盘,该光盘上包含各种 PBS Frontline 节目。使用电子表格解决方案,无法将光盘上的文件与光盘本身或光盘所在的文件夹关联起来,除非创建一个更复杂的电子表格。如果我想浏览收藏中的电视节目或搜索特定节目,电子表格解决方案就会变得非常麻烦。我想要一个可以搜索光盘内容以及光盘标题的解决方案。
本质上,实体关系如下:CD-Binders 包含 Discs,Discs 包含 Files。因此,Binders 包含 Discs 和 Files。Discs 属于一个 binder 和多个 files。Files 属于一个 binder 和一个 disc。请参阅下图

通过这种对物理对象的表示,就可以实现最初的目标。
准备用于编目的文件夹

上图展示了如何设置用于编目的文件夹。我在照片中添加了编号方案,以便更容易查看。使用黑色马克笔,我从 1 开始为每一页编号。如果文件夹每页有 4 个槽,那么每个槽都标记为 A 到 D。如果文件夹只有 2 个槽,则槽标记为 A 到 B。因此,在上图的第 3 页,CD-Binder 中的 sleeve 3A 指的是 CD-Binder 中第 3 页的第一个槽(左上角)。3B 指的是 CD-Binder 中第 3 页的第二个槽(右上角),以此类推。
文件夹封面已标记,并且文件夹已命名。此文件夹名为 Big-Bertha,总容量为 352 张光盘(88 页)。Big-Bertha 用于存储我家庭的 DVD 电影收藏。我还有一个名为 Boob-Tube 的 CD-Case,用于存储家庭的 Tivo 录像和其他各种光盘。CD-Cases 安全地存放在文件柜中,只有成年人可以访问它们。
数据库
好吧,每次我坐下来开始一个项目时,我都会纠结于这个选择。我的选择可能会限制受众,或者对某些用户来说根本无法访问。MySQL vs MS-SQL vs SQLite,等等。这次我决定尝试一些完全疯狂的事情,而不使用数据库。我一直对 XML 在过去几年的大规模采用感到着迷,因此我决定在这里尝试使用 XML 作为我的数据库。请注意;我将要展示的模式不是规范化的,并且违反了数据存储的所有最佳实践。我正在构建一个快速简单的解决方案,而不是火箭飞船,所以请原谅在此方面的合规性不足。
<?xml version="1.0" encoding="utf-8"?>
<database>
<binders />
<discs />
<files />
</database>
下面您可以看到一些示例数据
<?xml version="1.0" encoding="utf-8"?>
<database>
<binders>
<binder name="Big-Bertha" />
<binder name="Boob-Tube" />
</binders>
<discs>
<disc name="BBC Specials 2008 - Disc 01" label="VID01"
size="7446 MB" sleeve="1A" binder="Boob-Tube" comments="" />
<disc name="PBS Battlefield Vietnam" label="BFV"
size="7707 MB" sleeve="1B" binder="Boob-Tube" comments="" />
</discs>
<files>
<file name="BBC American Future - A History part 01.avi"
size="467 MB"
created="10/23/2008 12:00:34 AM"
modified="10/23/2008 12:00:34 AM"
path="E:\"
disc="BBC Specials 2008 - Disc 01"
binder="Boob-Tube" />
<file name="BBC American Future - A History part 02.avi"
size="467 MB"
created="10/25/2008 12:10:22 AM"
modified="10/25/2008 12:10:22 AM"
path="E:\"
disc="BBC Specials 2008 - Disc 01"
binder="Boob-Tube" />
<file name="BBC American Future - A History part 03.avi"
size="467 MB"
created="10/27/2008 2:44:32 PM"
modified="10/27/2008 2:44:32 PM"
path="E:\"
disc="BBC Specials 2008 - Disc 01"
binder="Boob-Tube" />
...
有趣的 XML CRUD 操作
我还没有多少机会与 LINQ 合作,到目前为止我对其非常满意。下面我将展示一些我在这款 XML 数据库中使用的有趣 CRUD 操作(我在此上下文中非常宽松地使用“数据库”一词)。
直接从 XML SELECT
我更喜欢使用 LINQ to Objects 而不是任何其他类型的 LINQ,因为它非常简单和优雅,但在少数情况下,我需要直接与 XML 文件通信,并且您可以在下面看到其中一个示例。在此例程中,我直接从 XML 文件中获取 binders,并使用它来填充我的 businessObject 实体(Binder、Disc、File 等)。
XDocument _xdoc = XDocument.Load(Globals.Database);
var bq = from f in _xdoc.Elements("database").Elements("binders").Elements("binder")
select f;
foreach (var cdcase in bq)
{
Binder cdbinder = new Binder();
cdbinder.Name = cdcase.Attribute("name").Value;
....
}
从实体 SELECT
您可以在下面看到 Binder
的一个成员,它确定用户输入的槽中是否已存储 Disc
,以便存储该 Disc
public static bool SleeveOccupied(string sleeve, string binderName)
{
Disc cd = null;
try
{
cd = (from d in Globals.Discs where d.Sleeve == sleeve &&
d.Binder.Name == binderName select d).Single();
}
catch (InvalidOperationException) { }
return (cd != null) ? true : false;
}
没有 LIKE 语句?
我在 LINQ 中没有找到类似于 T-SQL 语法中的 LIKE
语句。我决定尝试正则表达式,幸运的是,它们完美有效!
List<Disc> m_discs = new List<Disc>();
if (radioButton1.Checked && cbCase.SelectedIndex == 0)
{
m_discs = (from d in Globals.Discs
where Regex.IsMatch(d.Name, textBox1.Text, RegexOptions.IgnoreCase)
orderby d.Name
select d).ToList();
}
级联删除
这是一种简陋的模拟级联删除(它并不是真正的级联,但我假装它是!)。当用户删除一个光盘时,该光盘中的所有文件都需要一并删除。
XDocument xmldoc = XDocument.Load(Globals.Database);
string xpqd = String.Format("database/discs/disc[@name = '{0}']",
listView1.SelectedItems[0].Text);
string xpqf = String.Format("database/files/file[@disc = '{0}']",
listView1.SelectedItems[0].Text);
xmldoc.XPathSelectElement(xpqd).Remove();
xmldoc.XPathSelectElements(xpqf).Remove();
xmldoc.Save(Globals.Database);
listView1.SelectedItems[0].Remove();
Utility.LoadDb();
结论
构建这个系统非常有趣且简单;一个周末项目,结果出乎意料的好。查找光盘和归档文件不再是一件令人头疼的事情,而且由于一切都进展顺利,我有 30 分钟纯粹的快乐,用一把钢棒砸碎了我的 4 个 Disk Stakka 单元,看着它们碎成数百块;后来我把它们在篝火里熔化了!
历史
- 2009 年 3 月 31 日 - 提交文章