上期写的文章后面,林子朋友回复说要我看看他封装的ExcelManager,去看了一下,写的不错,功能上可以说比我的excelHelper强了好多,包括的字体,背景,模版等内容。要说明的是,我写的helper只提供了一个快速读取和写入的解决办法,并不是为了封装excel的操作而封装,只是因为内容的需要,为了使自己的东西用起来方便而已,并不能拿来作为一个单独的类库使用的,希望对我写的内容有兴趣朋友要注意这一点。就算最后形成了可重用的类库,其主要作用也是封装的导入导出,我期望告诉大家的是我的思考过程。
还有朋友提到要完整的代码和使用的例子,其实我一直认为看别人的代码是一件很累的事,不过看例子倒是一个不错的选择,那以后会增加一些实例。
Com.urp.command.FileFrameWork(6)
上期写的文章后面,林子朋友回复说要我看看他封装的ExcelManager,去看了一下,写的不错,功能上可以说比我的excelHelper强了好多,包括的字体,背景,模版等内容。要说明的是,我写的helper只提供了一个快速读取和写入的解决办法,并不是为了封装excel的操作而封装,只是因为内容的需要,为了使自己的东西用起来方便而已,并不能拿来作为一个单独的类库使用的,希望对我写的内容有兴趣朋友要注意这一点。就算最后形成了可重用的类库,其主要作用也是封装的导入导出,我期望告诉大家的是我的思考过程。
还有朋友提到要完整的代码和使用的例子,其实我一直认为看别人的代码是一件很累的事,不过看例子倒是一个不错的选择,那以后会增加一些实例。
这次先来说一下我的项目里是怎么使用这个helper类的。一般的helper类都是用静态方法来实现的,但是我写的这个并不是这样子做的,因为考虑到速度的问题,不想每次都要来打开关闭文件,这一点是不能作为一个单独的类库的又一个原因。访问excel提供com,ADO两种方法,而我把这两种方法封装在了一个类里面,那么形式上肯定是调用helper的类会有两个,所以就有了两个类ADOExcel,ComExcel。经过修改和原来的类图可能有不一样,FileType包就变成现在的样子了:
所有涉及到具体类型的读写处理放到AbstractFileAPI子类来实现,也就是ADOExcel,ComExcel作用我们举一个ComExcel的例子看一下,代码如下:
/**//// <summary>
/// ComExcell 的摘要说明。
/// </summary> public class ComExcel : AbstractFileAPI
{
public const string _fileType = "xls";
public const string _contentType = "application/vnd.ms-excel";
private ExcelHelper _helper;
private ArrayCollection _arrayCollection;
//将整个sheet装入ArrayCollection
private ArrayCollection GetValues(int rowEnd, int columnEnd)
{
Array columns = _helper.GetByRangeArray2(0, 0, 0, columnEnd);
Array rows = _helper.GetByRangeArray2(1, 0, rowEnd, columnEnd);
return new ArrayCollection(rows, columns);
}
//将ArrayCollection写回sheet
private void SetValues(int rowEnd, int columnEnd, Array values)
{
if(values != null)
{
_helper.SetByRangeArray2(0, 0, rowEnd, columnEnd, values);
}
}
public ComExcel(string path, string fileName) : base(path)
{
_fileName = fileName;
}
public ComExcel(Stream inputStream, string path, string fileName) : base(path)
{
_inputStream = inputStream;
_fileName = fileName;
_helper = new ExcelHelper();
}
//获取单元格
public override object Get(int rowIndex, int columnIndex)
{
return _arrayCollection.Get(rowIndex, columnIndex);
}
//获取行
public override object[] GetRowValue(int rowIndex)
{
return _arrayCollection.GetRow(rowIndex);
}
//新增行
public override void AddRowValue(object[] values)
{
_arrayCollection.AddRow(values);
}
//修改列名
public override void AddColumn(string columnName)
{
ColumnTexts.Add(columnName);
}
//获得列名,默认为sheet中的第0行
public override IList ColumnTexts
{
get
{
if(_columnTexts == null || _columnTexts.Count == 0)
{
_columnTexts = _arrayCollection.Columns;
}
return _columnTexts;
}
}
//打开一个现有sheet
public override void OpenFile()
{
try
{
_helper.OpenWorkBook(Path);
if(!_helper.OpenWorkSheet(0))
throw new Exception("找不到可用的Sheet, 本次导入不成功");
_arrayCollection = GetValues(_helper.UsedRowCount - 1, _helper.UsedColumnCount - 1);
}
catch(Exception ex)
{
throw ex;
}
}
//新建一个sheet
public override void NewFile()
{
try
{
this.Delete();
_helper.NewWorkBook();
if(!_helper.OpenWorkSheet(0))
throw new Exception("找不到可用的Sheet, 本次导入不成功");
_arrayCollection = new ArrayCollection();
}
catch(Exception ex)
{
throw ex;
}
}
//关闭文件
public override void CloseFile()
{
_helper.CloseSheet();
}
//保存并关闭文件
public override void CloseSaveFile()
{
SetValues(_arrayCollection.Count, ColumnCount - 1, _arrayCollection.ToArray2);
_helper.CloseSaveSheet(this.Path);
}
//列数
public override int ColumnCount
{
get
{
return _arrayCollection.Columns.Count;
}
}
//数据行数,不包括首行列名
public override int RowCount
{
get
{
return _arrayCollection.Count;
}
}
public override string FileType
{
get
{
return _fileType;
}
}
public override string ContentType
{
get
{
return _contentType;
}
}
}
然后涉及到流操作的那么都是有AbstractFileAPI来完成的,代码如下:
/**//// <summary>
/// AbstractFileAPI 的摘要说明。
/// </summary> public abstract class AbstractFileAPI : IFileAPI
{
private string _path;
private LocalFile _localFile = null;
protected string _fileName = null;
protected Stream _inputStream = null;
protected ArrayList _columnTexts = null;
protected AbstractFileAPI(string path)
{
_path = path;
_localFile = new LocalFile();
}
IFileAPI 成员#region IFileAPI 成员
public abstract object Get(int rowIndex, int columnIndex);
public abstract object[] GetRowValue(int rowIndex);
public abstract void AddRowValue(object[] values);
public abstract void AddColumn(string columnName);
public abstract IList ColumnTexts { get; }
public abstract int RowCount { get; }
public abstract int ColumnCount { get; }
public abstract void OpenFile();
public abstract void NewFile();
public abstract void CloseFile();
public abstract void CloseSaveFile();
//写入文件的流
public void WriteStream()
{
try
{
if(_localFile != null)
_localFile.Write(_inputStream, _path);
}
catch(Exception ex)
{
throw ex;
}
finally
{
}
}
//读取文件的流
public Stream ReadStream()
{
try
{
if(_localFile != null)
return _localFile.Read(_path);
return null;
}
catch(Exception ex)
{
throw ex;
}
finally
{
}
}
//新建文件
public void New()
{
try
{
if(_localFile != null)
_localFile.New(_path);
}
catch(Exception ex)
{
return ;
}
finally
{
if(_localFile != null)
_localFile.Close();
}
}
//删除文件
public void Delete()
{
try
{
if(_localFile != null)
_localFile.Delete(_path);
}
catch(Exception ex)
{
throw ex;
}
}
public void Open()
{
if(_localFile != null)
_localFile.Open(_path);
}
//关闭
public void Close()
{
try
{
if(_localFile != null)
_localFile.Close();
}
catch(Exception ex)
{
throw ex;
}
finally
{
if(_inputStream != null)
_inputStream.Close();
_inputStream = null;
_localFile = null;
_columnTexts = null;
}
}
public string GetResponse()
{
StringBuilder builder = new StringBuilder();
//写入列信息
for(int i = 0; i < this.ColumnCount; i++)
{
builder.Append(this.ColumnTexts[i].ToString() + "\t");
}
builder.Append("\n");
//写入数据信息
for(int i = 0; i < this.RowCount; i++)
{
for(int j = 0; j < this.ColumnCount; j++)
builder.Append(this.Get(i, j) + "\t");
builder.Append("\n");
}
return builder.ToString();
}
public abstract string FileType
{
get;
}
public abstract string ContentType
{
get;
}
public string FileName
{
get
{
return _fileName;
}
}
public string Path
{
get
{
return _path;
}
}
#endregion
}
现在就能看出ExcelHelper,ArrayCollection的作用了,ArrayCollection的作用就相当于table,用来保存数据的,ComExcel通过调用ExcelHelper这个代理来实现功能。
看到这里,如果一直关注我的文章的朋友可能会想,完成一个导入导出需要用到好多子类,又要考虑文件的类型,是Excel,Xml,mdb或者是别的,又要考虑使用的交换的类型,是List,还是DataTable。每种文件类型就是两个具体的类,如此多的子类如何实例化,如何组合起来,这也是一个比较烦人的问题。常用的工厂模式可能已经解决不好了,因为在FileType包的上面,还有一层DataAdapter包。DataAdapter访问FileType包,实例化具体的IFileAPI。原来写的时候就是在DataAdapter层来实例化一个FileType层的抽象工厂FileTypeFactory,然后再调用FileTypeFactory的子类来完成实例化,现在想想,这样做可能不最好的。毕竟DataAdapter不该去关心FileType层的事情,它只要调用IFileAPI接口里的方法就可以了。所以我们改用Builder模式来实现这一要求,如图:
AbstractBuilder类里有两组方法,一组是导入的时候调用的,一组是导出的时候调用的
再来举个例子,看一个初始化一个导入,Excel文件,的过程,交换类用 Collection。
1. 实例化一个AbstractBuilder,来决定使用何种交换类型
2. 实例化一个ImportDirector
ImportDirector director = new ImportDirector(fileUpload.InputStream, modelName, fileUpload.ContentType, builder);
3. 实例化IFileAPI, IdataAdapter
_dataAdapter = director.Build();
ImportDirector的Build()方法如下:
public IDataAdapter Build()
{
IFileAPI fileAPI = builder.InstanceFileAPI(inputStream, modelName, fileType);
IDataAdapter dataAdapter = builder.InstanceDataAdapter(modelName);
dataAdapter.FileAPI = fileAPI;
return dataAdapter;
}
这样子就把IFileAPI,IdataAdapter的实例化都提到了Builder包里,各自FileAPI层, DataAdapter层就不用去关心如何创建了。
freewiller.cnblogs.com 2006-9-12