ERP系统中要管理用户为单据上传的一些附件,比如增值税发票,退货发票,ROHS检测报告,这时,需要设计一个通用的附件管理模块来管理用户上传的附件。
数据表设计
IF OBJECT_ID ('dbo.Attachment') IS NOT NULL DROP TABLE dbo.AttachmentGOCREATE TABLE dbo.Attachment ( Index INT NOT NULL, MasterTable NVARCHAR (50) DEFAULT ('') NOT NULL, MasterKey DECIMAL (10) DEFAULT ((0)) NOT NULL, FileType NVARCHAR (10) DEFAULT ('') NOT NULL, FilePath NVARCHAR (250) NULL, CreatedDate DATETIME NULL, CreatedBy NVARCHAR (10) NULL, RevisedDate DATETIME NULL, RevisedBy NVARCHAR (10) NULL, Description NVARCHAR (60) NULL, KeySegment1 NVARCHAR (30) NULL, KeySegment2 NVARCHAR (30) NULL, KeySegment3 NVARCHAR (30) NULL, KeySegment4 NVARCHAR (30) NULL, KeySegment5 NVARCHAR (30) NULL, Size DECIMAL (18) NULL, File IMAGE NULL, UploadedBy NVARCHAR (10) NULL, UploadedDate DATETIME NULL, Md5Hash NVARCHAR (32) NULL, CONSTRAINT PK_Attachment PRIMARY KEY (Index,MasterTable,MasterKey) )GO
5个Key Segment是用来查找附件用的,相当于文章的关键字,用来标记此附件的大致内容。这里不提供附件内容的实际搜索功能,而只对设置的Key Segment进行查找与比较。我的另一个项目Data Solution对搜索存储于SQL Server中的文件字节流进行搜索,进行了探索和尝试。
4个必须要有的字段,Created By, Created Date, Revised By ,Revised Date用来记录创建和修改附件的用户,对IT审计有作用。ERP系统中的日记帐表基本上都有这4个字段供分析审计。
FileType 是自定义的可上传的文件类型,比如传真文件tif,图片文件jpg/bmp/gif,Office 办公文件Word/Excel/PPT
还有跨平台的通用文件格式PDF。
注意工具栏最后一个按钮上面有个数字8,表示此客户有附带8个附件文件,点击该按钮,弹出附件管理窗口
工具栏中有新增,删除按钮,可对附件数据进行操作。
当附件的数量相当多的时候,还需要写一个通用的附件浏览工具,以查看附件内容。
执行功能SAMFAB或SQAPTR,打开附件浏览器程序。
找到客户编号001,可以从图中看到,它有8个附件,在这里以附件类型进行了分组呈现。
双击一个附件图标文件,File Explorer可为您打开它的内容,进行在线浏览。如果想把附件保存到本地,可以选中附件,点工具栏中的Export按钮,弹出另存为对话框,为您保存附件文档。
左边的树是从一个配置文件中读取的,它保存数据表的分类以供加载到不同的模块组别中。
读取这个文件的方式是Linq to xml技术,自从掌握了Linq技术之后,简单的几句话完成复杂的读取和写入功能,代码简洁,容易维护。
string sql = Kingston.SystemAdminstration.Properties.Resources.LibraryExplorer;byte[] array = Encoding.ASCII.GetBytes(sql);MemoryStream stream = new MemoryStream(array); XDocument doc = XDocument.Load(stream);string library = doc.Root.Name.ToString();treeView.BeginUpdate();treeView.Nodes.Clear();UltraTreeNode root=new UltraTreeNode(library,library);root.Tag = "Library";root.LeftImages.Add(imgList.Images[0]);foreach(XElement node in doc.Root.Elements()) { UltraTreeNode module = new UltraTreeNode(node.Name.ToString(), node.Name.ToString()); module.Tag = "Module"; module.LeftImages.Add(imgList.Images[1]); foreach (XElement table in node.Elements()) { UltraTreeNode tableNode = new UltraTreeNode(table.Attribute("Name").Value, table.Attribute("Name").Value); tableNode.Tag = "Table"; tableNode.LeftImages.Add(imgList.Images[2]); module.Nodes.Add(tableNode); } root.Nodes.Add(module); }treeView.Nodes.Add(root);treeView.EndUpdate();
以资源文件嵌入到程序集中,读取时它是个字符串,XDocument可接受一个文件名,或是一个字节流,所以开头几句我把它转化为流来供Linq解析。
在系统设置中,有一个参数是控制附件的保存方式。系统是保存附件的路径(File Path),还是保存附件的内容(字节流)。
如图所示,Control Checklist的最后一项设定 Attachment Save into Database,即把附件的字节流保存到数据(Image)字段中,此选项会影响数据库大小,但也有好的移植性。
以文件保存在本地为例,它只是对文件进行一个简单的Copy动作,代码如下所示
AttachmentEntity attachment = item.Tag as AttachmentEntity;SaveFileDialog saveDialog = new SaveFileDialog();saveDialog.Filter = "Excel 97-2003|*.xls|Microsoft Word|*.doc|Adobe PDF|*.pdf|All File|*.*";saveDialog.FileName = attachment.FilePath;if (saveDialog.ShowDialog() == DialogResult.OK){ string fileName = saveDialog.FileName; if (!string.IsNullOrWhiteSpace(attachment.FilePath)) { File.Copy(attachment.FilePath, saveDialog.FileName); }}
VB或DELPHI程序的习惯,会拖动一个SaveFileDialog 组件(component)到界面中,然后在这里直接引用。我倾向于在这里直接创建一个SaveFileDialog ,用完了让它Dispose。use it and burn it,电影里面讲到人与人的雇佣关系时,常用这个句子,用完了就抛弃。