65.9K
CodeProject 正在变化。 阅读更多。
Home

CD Beaver -光盘介质存储管理

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.75/5 (4投票s)

2009年3月31日

CPOL

6分钟阅读

viewsIcon

32897

downloadIcon

341

用于归档和管理光盘介质的解决方案

CD-Beaver

引言

CD-Beaver 是一个用于编目光盘介质(如 CD 和 DVD)的简单程序。该应用程序使用 XML 作为其后端数据存储,并且是光盘收纳盒的绝佳搭档。CD Beaver 的目的是简化存储和查找光盘及其包含文件的过程。在本文中,我将演示 CD Beaver 的各种用途以及它如何使媒体存档更加轻松和减轻压力。

背景

与我大多数的个人项目一样,这个项目源于极度的沮丧。几年前,我的一位拥有小型企业的朋友正在寻找一种自动化的光盘存储系统。他很快就发现了 Opdicom 公司的一款名为 Disk Stakka 的解决方案。下面您可以看到一个类似他的设置;五个单元堆叠在一起,以 625 美元(每个单元 125 美元)的合理价格提供了 500 张光盘的存储解决方案。

stackka.gif

经过几个月的试用,他又购买了 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 日 - 提交文章
© . All rights reserved.