知识管理系统Data Solution研发日记之三 文档解决方案
前面两篇文章已经展示了Data Solution的设计目标和为达到此目标而设计的应用程序,这一篇继续细化对它的介绍,讲解Data Solution如何对文件进行扫描,转换,存储,检索。
本机磁盘的文件格式通常是doc/docx,pdf,pst/ost/eml,htm/mht,txt/rtf,这几种格式是电脑中常用的格式。要达到在同一个编辑器Editor中可以对它们进行编辑,再整理,需要选择一种通用的格式,把其它的格式转化为这种通用格式,对这种格式进行编辑。DOC/DOCX格式功能强大,有很多开源类型库对它进行读写,RTF格式也包含了丰富数据资料,重要的一点是RTF格式,是一种开放的格式。可以到微软的网站上下载《Microsoft Office Word 2003 Rich Text Format (RTF) Specification》,以熟悉这种文件格式。开放格式的好处之一是,在后续的进一步开发中,可以找到很多现有的功能和代码,包括开放的源码。所以,Data Solution选择RTF格式作为文件存储的标准格式。
下一个目标就是要找到RTF格式的编辑工具,通过Google可以找到很多RTF格式的编辑器,对RTF格式进行直接编辑,存储和转换。因为是开放格式,微软的.NET Framework自带了RichTextBox,可用于RTF格式的编辑。不过,这个控件还需要强化,可以到CodeProject.com上找到一些工具和文章。
这两个关键的步骤解决了,下面就是设计数据库和设计数据读写代码。创建Document数据库,文档表的脚本如下
CREATE TABLE [dbo].[DOCUMENT]( [RECNUM] [int] IDENTITY(1,1) NOT NULL, [SUBJECT] [nvarchar](2000) NULL, [BODY_TYPE] [nvarchar](50) NULL, [BODY] [nvarchar](max) NULL, [CREATE_DATE] [datetime] NULL, [CREATE_BY] [nvarchar](50) NULL, [REVISED_Date] [datetime] NULL, [REVISED_BY] [nvarchar](50) NULL, [CATEGORY] [int] NULL, [COMPUTER] [nvarchar](200) NULL, [PATH] [nvarchar](2000) NULL, CONSTRAINT [PK_DOCUMENT] PRIMARY KEY CLUSTERED ( [RECNUM] ASC )WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY] ) ON [PRIMARY]
Body就是存储文档的表字段,设计它为NVARCHAR(MAX)类型,CATEGORY用于文档的分类查找,SUBJECT可存放关键字或是标题,BODY_TYPE用于全文搜索,存储文档类型扩展名。COMPUTER和PATH用于在本机扫描中,指定原始文档的路径,可用于追踪。
Data Solution系统选择LLBL Gen Framework作为数据库访问层的代码生成器,生成解决方案的代码如下图
来看一下,对文档进行保存的一段代码,它是标准的LLBL Gen Framework的routine代码
public DocumentEntity SaveDocument(DocumentEntity doc) { using (DataAccessAdapter adapter = GetDataAccessAdapter()) { try { adapter.StartTransaction(IsolationLevel.ReadCommitted, "SaveDocument"); adapter.SaveEntity(doc, true, false); adapter.Commit(); } catch { adapter.Rollback(); throw; } } return doc; }
所谓routine代码,就是代码可以由模板生成的,就好比上下班打卡一样,是很平常的简单的行为。
注意这里的throw代码,它没有写成这样
catch(Exception ex) { adapter.Rollback(); throw ex; }
在《.NET框架程序设计》一书中,解释了这两个throw的区别,它们会产生不同的stack trace,异常的起始点不同。
基础层面的问题解决了,下面的应用程序就水到渠成,以不同的方式导入文档到数据库中。
Batch Import 批次导入指定目录的文件到数据库中
Doc Scanner 批次导入指定格式的文件到数据库中
PDF Watcher 专用于PDF文件格式的转换,导入,因为是Watcher,所以你肯定会想到是个FileSystemWatcher
Doc Loader 适用于单个文档的转换,导入,一次只处理一个文档
再来看数据库中的文档的展示,Document Explorer会展现导入进数据库的原始文件,在这里可以进行预览,删除,分类。分类之后,这个文档就好比打上了合格的标签一样,可以在以后的程序中进一步使用。否则,不分类的文件都会只停留在这里,后继的步骤无法处理。这是个文档流程上的的Policy,如果不喜欢这个步骤,可以去掉。
Document Browser 分类查看文档
左边是树型结构,右边是从属于这个分类的文档。如果要对扫描进数据库中的文件进行分类,可以这样操作
在Document Explorer中选中一个或多个文件,点击右键Category Document
在Document Browser的左边的树中,右键Paste Document
之后就看到了效果,在.NET结点下面,展示了所Paste的文档及其属性
Document Browser左边的树是文档的分类,可对它对进新增子节点,新增加一个分类的效果如下图
Category是取自数据库中的类别表,它的脚本定义如下
CREATE TABLE [dbo].[CATEGORY]( [RECNUM] [int] IDENTITY(1,1) NOT NULL, [NAME] [nvarchar](200) NULL, CONSTRAINT [PK_CATEGORY] PRIMARY KEY CLUSTERED ( [RECNUM] ASC )WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY] ) ON [PRIMARY]
这颗文档分类树,它的结构定义是放到本地的XML文件中,上图中所看到的树的格式定义如下
如上图所示,tag就是从数据库中获取的分类定义,依据这个标识,以检索分类的文档。在Document Browser窗体的打开与关闭时,它会持久化树的结构定义,代码如下
protected override void OnClosed(EventArgs e) { base.OnClosed(e); TreeViewSerializer serializer = new TreeViewSerializer(); serializer.SerializeTreeView(this.treeView, treeFile); } protected override void OnLoad(EventArgs e) { treeView.ImageList = this.imageList; TreeNode root=treeView.Nodes[0]; if (File.Exists(treeFile)) { treeView.Nodes.Clear(); TreeViewSerializer serializer = new TreeViewSerializer(); serializer.DeserializeTreeView(this.treeView, treeFile); treeView.ExpandAll(); } }
如果你对如何把树节点定义保存到文件系统中感兴趣,可以用关键字TreeViewSerializer在CodeProject中查找,这里的代码就是取自其中一篇文章的代码。
还有另一个地方,会用到树型结构分类,在Editor编辑器的打开文件对话框中,如下图所示
这里的效果,与Document Browser的效果是完全一样的。原来以为是可以用Custom Open File Dialog来解决Open File对话框的Place Bar的问题,也就是上图中看到的左边的树的分类,也是如下图中红色边框包围的地方
要达到重写红色区域,Windows有规定的路径,并且会验证这个路径,这个办法没有通过,无奈之下才用的自定义对话框。之前见到过的软件,SharePoint Designer重写过这个Place Bar区域,可惜没有领悟它的实现原理。
写到这里,还没有完成,这里还需要一点OFFICE 二次开发的知识,在OFFICE软件中,写入一个插件,可以把正在浏览的文档,直接导入到我的文档数据库中,如下图所示
这里已经安装了两个插件,Nitro PDF Professional和Acrobat,用于把当前DOC/DOCX文档转换为PDF文件。所以,还需要写一个把当前的DOC/DOCX文档转存到文档数据库中的插件。
在把PDF转化为可以编辑的RTF格式过程中,遇到了不少的麻烦。把DOC/DOCX转换成PDF,这个行为,有很多开源代码可以借用,但是,倒过来,把PDF转换成DOC/DOCX,这个组件却不好找。有一种方案是把PDF转化为TIFF,然后再用OCR软件系统(ABBYY FineReader 9)来转成DOC/DOCX文件。我们做程序员的穷,没有那么多银子买昂贵的SDK License,转向寻找Crack或是Patch之类的,也没有结果。这些业界领先的技术,连Trial版本都不会出现在网上,根本没有机会Trial一把,或是把它放到虚拟机里面,永远以Trial的方式来使用。有的组件,比如PDF Focus.NET,可以试用一把,可是转换出来的文件,要么加上了Trial的水印,要么只能转换前三页,后面的都不能转换。或者有的是ActiveX版本的控件,OCX方式注册到系统中,总有这样那样的问题。杯具,想想以后要离开对Microsoft .NET Framework的依赖,离开对SQL Server的依赖,以这次的经验来看,这日子是没法过了。尽管我已经解决了这里的所有问题,仍然不愿意面对,在非数据库开发的领域,技术和知识是很值钱的。因为我们已经习惯了在数据库领域的开发,技术和知识是一文不值的,只有做出的产品才值钱,悲剧。