Convierte DataTable en secuencia CSV

Actualmente tiene una DataTable, pero desea transmitirla al usuario a través de un WebHandler. FileHelpers tiene CommonEngine.DataTableToCsv(dt, "file.csv") . Sin embargo, lo guarda en un archivo. ¿Cómo puedo guardarlo en una transmisión en su lugar? Sé cómo hacerlo cuando conozco las columnas avanzadas o no cambian, pero quiero generar los encabezados de las columnas directamente desde la tabla de datos.

Si conozco las columnas, solo creo la clase:

 [DelimitedRecord(",")] public class MailMergeFields { [FieldQuoted()] public string FirstName; [FieldQuoted()] public string LastName; } 

Luego use FileHelperEngine y agregue los registros:

 FileHelperEngine engine = new FileHelperEngine(typeof(MailMergeFields)); MailMergeFields[] merge = new MailMergeFields[dt.Rows.Count + 1]; // add headers merge[0] = new MailMergeFields(); merge[0].FirstName = "FirstName"; merge[0].LastName = "LastName"; int i = 1; // add records foreach (DataRow dr in dt.Rows) { merge[i] = new MailMergeFields(); merge[i].FirstName = dr["Forename"]; merge[i].LastName = dr["Surname"]; i++; } 

Finalmente escribe en una secuencia:

 TextWriter writer = new StringWriter(); engine.WriteStream(writer, merge); context.Response.Write(writer.ToString()); 

Lamentablemente, como no conozco las columnas anteriores, no puedo crear la clase de antemano.

Puedes escribir algo rápidamente tú mismo:

 public static class Extensions { public static string ToCSV(this DataTable table) { var result = new StringBuilder(); for (int i = 0; i < table.Columns.Count; i++) { result.Append(table.Columns[i].ColumnName); result.Append(i == table.Columns.Count - 1 ? "\n" : ","); } foreach (DataRow row in table.Rows) { for (int i = 0; i < table.Columns.Count; i++) { result.Append(row[i].ToString()); result.Append(i == table.Columns.Count - 1 ? "\n" : ","); } } return result.ToString(); } } 

Y para probar:

  public static void Main() { DataTable table = new DataTable(); table.Columns.Add("Name"); table.Columns.Add("Age"); table.Rows.Add("John Doe", "45"); table.Rows.Add("Jane Doe", "35"); table.Rows.Add("Jack Doe", "27"); var bytes = Encoding.GetEncoding("iso-8859-1").GetBytes(table.ToCSV()); MemoryStream stream = new MemoryStream(bytes); StreamReader reader = new StreamReader(stream); Console.WriteLine(reader.ReadToEnd()); } 

EDIT: Re sus comentarios:

Depende de cómo quiera que se formatee su csv, pero generalmente, si el texto contiene caracteres especiales, desea encerrarlo entre comillas dobles, es decir, "mi, texto". Puede agregar la verificación en el código que crea el csv para verificar si hay caracteres especiales y adjunta el texto entre comillas dobles, si es así. En cuanto a lo de .NET 2.0, simplemente créelo como un método auxiliar en su clase o elimine la palabra this en la statement de método y llámela así: Extensions.ToCsv (table);

Actualización 1

Lo modifiqué para usar StreamWriter, agregue una opción para verificar si necesita encabezados de columna en su salida.

 public static bool DataTableToCSV(DataTable dtSource, StreamWriter writer, bool includeHeader) { if (dtSource == null || writer == null) return false; if (includeHeader) { string[] columnNames = dtSource.Columns.Cast().Select(column => "\"" + column.ColumnName.Replace("\"", "\"\"") + "\"").ToArray(); writer.WriteLine(String.Join(",", columnNames)); writer.Flush(); } foreach (DataRow row in dtSource.Rows) { string[] fields = row.ItemArray.Select(field => "\"" + field.ToString().Replace("\"", "\"\"") + "\"").ToArray(); writer.WriteLine(String.Join(",", fields)); writer.Flush(); } return true; } 

Como puede ver, puede elegir la salida por StreamWriter inicial, si usa StreamWriter (Stream BaseStream), puede escribir csv en MemeryStream, FileStream, etc.

Origen

Tengo una función de datatable a csv fácil, me sirve bien:

  public static void DataTableToCsv(DataTable dt, string csvFile) { StringBuilder sb = new StringBuilder(); var columnNames = dt.Columns.Cast().Select(column => "\"" + column.ColumnName.Replace("\"", "\"\"") + "\"").ToArray(); sb.AppendLine(string.Join(",", columnNames)); foreach (DataRow row in dt.Rows) { var fields = row.ItemArray.Select(field => "\"" + field.ToString().Replace("\"", "\"\"") + "\"").ToArray(); sb.AppendLine(string.Join(",", fields)); } File.WriteAllText(csvFile, sb.ToString(), Encoding.Default); } 

Si puede convertir su tabla de datos en un IEnumerable, esto debería funcionar para usted …

  Response.Clear(); Response.Buffer = true; Response.AddHeader("content-disposition", "attachment;filename=FileName.csv"); Response.Charset = ""; Response.ContentType = "application/text"; Response.Output.Write(ExampleClass.ConvertToCSV(GetListOfObject(), typeof(object))); Response.Flush(); Response.End(); public static string ConvertToCSV(IEnumerable col, Type type) { StringBuilder sb = new StringBuilder(); StringBuilder header = new StringBuilder(); // Gets all properies of the class PropertyInfo[] pi = type.GetProperties(); // Create CSV header using the classes properties foreach (PropertyInfo p in pi) { header.Append(p.Name + ","); } sb.AppendLine(header.ToString().Remove(header.Length)); foreach (object t in col) { StringBuilder body = new StringBuilder(); // Create new item foreach (PropertyInfo p in pi) { object o = p.GetValue(t, null); body.Append(o.ToString() + ","); } sb.AppendLine(body.ToString().Remove(body.Length)); } return sb.ToString(); } 

No sé si esta conversión de VB a C # está bien, pero si no quieres las comillas en torno a tus números, puedes comparar el tipo de datos de la siguiente manera.

 public string DataTableToCSV(DataTable dt) { StringBuilder sb = new StringBuilder(); if (dt == null) return ""; try { // Create the header row for (int i = 0; i <= dt.Columns.Count - 1; i++) { // Append column name in quotes sb.Append("\"" + dt.Columns[i].ColumnName + "\""); // Add carriage return and linefeed if last column, else add comma sb.Append(i == dt.Columns.Count - 1 ? "\n" : ","); } foreach (DataRow row in dt.Rows) { for (int i = 0; i <= dt.Columns.Count - 1; i++) { // Append value in quotes //sb.Append("""" & row.Item(i) & """") // OR only quote items that that are equivilant to strings sb.Append(object.ReferenceEquals(dt.Columns[i].DataType, typeof(string)) || object.ReferenceEquals(dt.Columns[i].DataType, typeof(char)) ? "\"" + row[i] + "\"" : row[i]); // Append CR+LF if last field, else add Comma sb.Append(i == dt.Columns.Count - 1 ? "\n" : ","); } } return sb.ToString; } catch (Exception ex) { // Handle the exception however you want return ""; } } 

Si desea transmitir el archivo CSV al usuario sin crear un archivo, creo que el siguiente es el método más simple. Puede usar cualquier extensión / método para crear la función ToCsv () (que devuelve una cadena basada en la DataTable dada).

  var report = myDataTable.ToCsv(); var bytes = Encoding.GetEncoding("iso-8859-1").GetBytes(report); Response.Buffer = true; Response.Clear(); Response.AddHeader("content-disposition", "attachment; filename=report.csv"); Response.ContentType = "text/csv"; Response.BinaryWrite(bytes); Response.End(); 

He usado el siguiente código, saqueado del blog de alguien (por favor, perdonen la falta de cita). Se encarga de las cotizaciones, línea nueva y coma de una manera razonablemente elegante al cotizar cada valor de campo.

  ///  /// Converts the passed in data table to a CSV-style string. ///  /// Table to convert /// Resulting CSV-style string public static string ToCSV(this DataTable table) { return ToCSV(table, ",", true); } ///  /// Converts the passed in data table to a CSV-style string. ///  /// Table to convert /// true - include headers
/// false - do not include header column /// Resulting CSV-style string public static string ToCSV(this DataTable table, bool includeHeader) { return ToCSV(table, ",", includeHeader); } /// /// Converts the passed in data table to a CSV-style string. /// /// Table to convert /// true - include headers
/// false - do not include header column /// Resulting CSV-style string public static string ToCSV(this DataTable table, string delimiter, bool includeHeader) { var result = new StringBuilder(); if (includeHeader) { foreach (DataColumn column in table.Columns) { result.Append(column.ColumnName); result.Append(delimiter); } result.Remove(--result.Length, 0); result.Append(Environment.NewLine); } foreach (DataRow row in table.Rows) { foreach (object item in row.ItemArray) { if (item is DBNull) result.Append(delimiter); else { string itemAsString = item.ToString(); // Double up all embedded double quotes itemAsString = itemAsString.Replace("\"", "\"\""); // To keep things simple, always delimit with double-quotes // so we don't have to determine in which cases they're necessary // and which cases they're not. itemAsString = "\"" + itemAsString + "\""; result.Append(itemAsString + delimiter); } } result.Remove(--result.Length, 0); result.Append(Environment.NewLine); } return result.ToString(); }

Puedes intentar usar algo como esto. En este caso, utilicé un procedimiento almacenado para obtener más tablas de datos y exportar todas usando CSV.

 using System; using System.Text; using System.Data; using System.Data.SqlClient; using System.IO; namespace bo { class Program { static private void CreateCSVFile(DataTable dt, string strFilePath) { #region Export Grid to CSV // Create the CSV file to which grid data will be exported. StreamWriter sw = new StreamWriter(strFilePath, false); int iColCount = dt.Columns.Count; // First we will write the headers. //DataTable dt = m_dsProducts.Tables[0]; for (int i = 0; i < iColCount; i++) { sw.Write(dt.Columns[i]); if (i < iColCount - 1) { sw.Write(";"); } } sw.Write(sw.NewLine); // Now write all the rows. foreach (DataRow dr in dt.Rows) { for (int i = 0; i < iColCount; i++) { if (!Convert.IsDBNull(dr[i])) { sw.Write(dr[i].ToString()); } if (i < iColCount -1 ) { sw.Write(";"); } } sw.Write(sw.NewLine); } sw.Close(); #endregion } static void Main(string[] args) { string strConn = "connection string to sql"; string direktorij = @"d:"; SqlConnection conn = new SqlConnection(strConn); SqlCommand command = new SqlCommand("sp_ado_pos_data", conn); command.CommandType = CommandType.StoredProcedure; command.Parameters.Add('@skl_id', SqlDbType.Int).Value = 158; SqlDataAdapter adapter = new SqlDataAdapter(command); DataSet ds = new DataSet(); adapter.Fill(ds); for (int i = 0; i < ds.Tables.Count; i++) { string datoteka = (string.Format(@"{0}tablea{1}.csv", direktorij, i)); DataTable tabela = ds.Tables[i]; CreateCSVFile(tabela,datoteka ); Console.WriteLine("Generišem tabelu {0}", datoteka); } Console.ReadKey(); } } } 
 public void CreateCSVFile(DataTable dt, string strFilePath,string separator) { #region Export Grid to CSV // Create the CSV file to which grid data will be exported. StreamWriter sw = new StreamWriter(strFilePath, false); int iColCount = dt.Columns.Count; for (int i = 0; i < iColCount; i++) { sw.Write(dt.Columns[i]); if (i < iColCount - 1) { sw.Write(separator); } } sw.Write(sw.NewLine); // Now write all the rows. foreach (DataRow dr in dt.Rows) { for (int i = 0; i < iColCount; i++) { if (!Convert.IsDBNull(dr[i])) { sw.Write(dr[i].ToString()); } if (i < iColCount - 1) { sw.Write(separator); } } sw.Write(sw.NewLine); } sw.Close(); #endregion } 

La respuesta de BFree funcionó para mí. Necesitaba publicar la transmisión directamente en el navegador. Lo que me imagino es una alternativa común. Agregué lo siguiente al código Main () de BFree para hacer esto:

 //StreamReader reader = new StreamReader(stream); //Console.WriteLine(reader.ReadToEnd()); string fileName = "fileName.csv"; HttpContext.Current.Response.ContentType = "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"; HttpContext.Current.Response.AddHeader("content-disposition", string.Format("attachment;filename={0}", fileName)); stream.Position = 0; stream.WriteTo(HttpContext.Current.Response.OutputStream);