从读取Excel文件引申出的问题(上)
事件起因源于偶然在百度知道看到一个提问,说是他的Excel文件中共有几十万行的数据,希望可以将表中重复内容删除,并将内容分离为每个Excel文件5000行数据。然后就有了这个测试,也许是因为思维发散的缘故,不知道,怎么的,就转到了处理从Excel文件中读取图片上来,事情的发展出乎我的意料,看来不够专注,不过这也让我有了意外的收获。
经过分析得出,不管是采用OleDbConnection还是采用Application对象,都是一样的需要加载Excel文件(纯粹废话),首先来了一段OleDbConnection读取的代码。
OleDbConnection conn =new OleDbConnection(connection);
conn.Open();
DataTable=conn.GetOleDbSchemaTable(OleDbSchemaGuid.Tables, newobject[] { null,null, null, "Table" });
上面的代码读取了在某个excel文件中存在所有工作簿,实际上是获取了excel文件的架构,即Sheet,这样可达到动态读取excel文件中所有内容的目的。
再看如何填充
{
Stringtext=string.Format("selec*from {0}", dt.Rows[i]["TABLE_NAME"]);
OleDbDataAdaptermyCommand =new OleDbDataAdapter(text,connection);
DataSetmyDataSet =new DataSet();
myCommand.Fill(myDataSet);
}
上面的代码是把所有工作簿都填充为一个DataSet,这里仅作演示,因为我知道我的excel文件只有一个工作簿,当然实际中我们会分开的填充的。填充完毕后,我打开数据集一看,我被震精了,数据集中只有字符串内容,而并没有图片。这相当让我崩溃。
再换一个角度,用com对象来读取,思路和上面的一致,加载Excel文件,读取字符串数据。
Excel.Workbook workbook = excel.Workbooks.Add(filePath);
excel.UserControl =true;
excel.Visible =false;
for (int i =0; i < workbook.Worksheets.Count; i++)
{
System.Text.StringBuilder sb =new System.Text.StringBuilder();
Excel.Worksheet sheet = workbook.Worksheets.get_Item(i +1) as Excel.Worksheet;
for (int row =2; row <= sheet.UsedRange.Rows.Count; row++)
{
//取单元格值;
for (int col =1; col <= sheet.UsedRange.Columns.Count; col++)
{
Microsoft.Office.Interop.Excel.Range range = sheet.Cells[row, col] as Excel.Range;
sb.Append(","+ col.ToString() +":"+ range.Text);
}
sb.Append(System.Environment.NewLine);
Console.WriteLine(sb.ToString());
//取存图片;
if (sheet.Shapes.Count >0)
{
Bitmap picture;
IDataObject data=null;
foreach (Excel.Shape item in sheet.Shapes)
{
item.Copy();
data= Clipboard.GetDataObject();
if (null!= data)
{
picture = (Bitmap) data.GetData(DataFormats.Bitmap);
picture.Save(string.Format(@"D:\temp\aa\{0}.jpg", row));
}
}
}
}
}
workbook.Close(false, null, null);
excel.Quit();
上面的代码是达到了读取数据和图片的要求,可实在是太慢,特别是对图片的读取上,这对内存一来一往的Copy和Paste,着实让我无法忍受,明明想要的东西就在眼前,这还要跑两次内存读取,相当的无语。
再次调试,显示IL后发现,我决定使用Shape.CopyPicture()方法,因为我发现Copy和CopyPicture方法实际上调用的是不同的实现,Copy是不管你是什么东西,一概Copy到内存中,CopyPicture则是可以按照一定的外观和格式Copy过去,当然,原理上说,应该是Copy速度更快一些。
曾经冒出过一个 极其可笑兼常用的方式,直接把IComObject对象转换为托管对象,如下:
Bitmapimg=(Bitmap)data
这里直接失败,别说运行,编译都不通过。
再又冒出了一个把对象序列化流的想法,希望流的速度快些,结果发现Shape对象不支持序列化,意思是这条路也走不通了,最后看来,目前只能使用内存复制粘贴的方式来执行了。好吧,我还不相信没有别的办法了,等下回我再来收拾你。