最近公司需求做一个直接根据数据库表导出csv的功能,要求可以多个表同时导入到一个csv文件里,通过查阅相关资料后,终于实现了该功能,期间也遇到过一些问题在此总结一二。
首先为了要获得数据库的表数据,写了一个通用方法,代码如下:
1 public static DataSet GetTableAll(List<string> tableAll) 2 { 3 4 using (SqlConnection conn = new SqlConnection(connectionString)) 5 { 6 DataSet ds = new DataSet(); 7 foreach (string table in tableAll) 8 { 9 DataTable dt = new DataTable(); 10 using (SqlDataAdapter da = new SqlDataAdapter("Select * from " + table, conn)) 11 { 12 da.Fill(dt); 13 } 14 ds.Tables.Add(dt); 15 } 16 return ds; 17 } 18 }
返回的是一个ds集合,参数用的是List集合,这样就可以返回需要导出的表的集合。调用方法代码如下:
List<string> tableAll = new List<string>(); tableAll.Add("tableName"); tableAll.Add("tableName"); DataSet tableName = CommonClass.GetTableAll(tableAll);
CSVUtility.GetCSV(tableName, this.Page, 文件名);
代码比较简单就不在赘述了。最后就是最重要的导出方法了,代码如下:
public static void GetCSV(DataSet data, Page page,String fileName) { MemoryStream stream = new MemoryStream(); foreach (DataTable dt in data.Tables) { string[] filedsToExport = new string[dt.Columns.Count]; for (int i = 0; i < dt.Columns.Count; i++) { filedsToExport[i] = dt.Columns[i].ColumnName; } GetCSV(filedsToExport, dt, stream); } page.Response.ContentType = "application/csv"; page.Response.Clear(); page.Response.Buffer = true; page.Response.ContentEncoding = Encoding.UTF8; page.Response.Charset = "UTF-8"; page.Response.AddHeader("Content-Disposition", "attachment;filename=" + fileName); page.Response.Cache.SetCacheability(HttpCacheability.NoCache); //防止中文乱码 page.Response.BinaryWrite(new byte[] { 0xEF, 0xBB, 0xBF }); page.Response.BinaryWrite(stream.ToArray()); page.Response.Flush(); page.Response.End(); }
上面方法也比较简单,就是注释下面那行代码必须加上,否则会出现中文乱码现象,因为csv是纯文本文件,有个BOM方式编码问题。在上面方法中还调用了一个方法,通过MemoryStream对象流把数据写入了内存,具体代码如下:
private static void GetCSV(string[] filedsToExport, DataTable data, MemoryStream stream) { using (var sw = new StreamWriterX(stream, false)) { for (int i = 0; i < filedsToExport.Length; i++) { if (i != 0) { sw.Write(","); } sw.Write("\""); sw.Write(filedsToExport[i].Replace("\"", "\"\"")); sw.Write("\""); } sw.Write("\n"); foreach (DataRow row in data.Rows) { for (int i = 0; i < filedsToExport.Length; i++) { if (i != 0) { sw.Write(","); } sw.Write("\""); sw.Write(row[filedsToExport[i]].ToString() .Replace("\"", "\"\"")); sw.Write("\""); } sw.Write("\n"); } sw.WriteLine("****************************The End********************************"); } } //防止Writer关闭的时候自动关闭数据流 class StreamWriterX : StreamWriter { public StreamWriterX(Stream stream, Boolean closeable) : base(stream) { var fi = typeof(StreamWriter).GetField("closable", BindingFlags.Instance | BindingFlags.NonPublic); if (fi != null) fi.SetValue(this, closeable); } }
上面这个方法其实有几个地方需要讲述下,第一个就是write的时候要注意csv转换格式的问题,我在末尾的时候加了一个WriteLine,因为多个表数据写入到同一个csv的时候,需要区分表与数据字段(暂时需求),比较重要的是SreamWriterX这个类,因为在Writer关闭的时候会自动关闭数据流,导致程序出错。这个问题折腾了好久,最后通过高人指导才算解决了这个问题,有人说通过重写Disponse也可以实现,不过具体的还没有测试过。
虽然是一个小功能,期间尝试了各种方法,折腾了也挺蛮久的,不过确实收获了一些,所以分享出来与大家共勉,如果你有更好的解决方案,不吝赐教!