2010-08-20 48 views
7

OK. Voglio creare un file .csv in C#. Mi sono guardato attorno e ho notato che molte persone stanno usando il sistema. IIO.Memorystream e system.io.streamwriter.Creare un file .csv in C#

Il problema è questo: ho un'applicazione web. Voglio dare all'utente la possibilità di esportare in Excel. Il problema è che Excel non può essere installato sul server (non chiedere). Voglio essere in grado di scrivere un foglio di esportazione in formato .csv per un report. Ora, le intestazioni e i dati dei report saranno diversi per tutti i report (il looping risolverà questo). Qualcuno ha un esempio o risorse migliori da affrontare?

+0

Dopo aver letto in giro. Tutto ciò di cui ho veramente bisogno è di eseguire il ciclo e generare i dati separati da virgole con/n alla fine della riga e continuare a scorrere fino al termine ... salvare il file come .csv e quindi chiudere lo stream ... corretto? – Tom

+0

È inoltre necessario impostare il tipo di contenuto, e se un riferimento potrebbe contenere un carattere "", ',' o di nuova riga è necessario sfuggire anche a esso. Codice per quello nella mia risposta –

risposta

13

Questo è l'approccio che prendo normalmente. Probabilmente non il più efficiente però.

 /// <summary> 
    /// Generates the contents of the log file. 
    /// </summary> 
    /// <returns>The contents of the log file.</returns> 
    internal string GenerateLogFile() 
    { 
     StringBuilder csvExport = new StringBuilder(); 
     csvExport.AppendLine(Resources.CSVHeader); 

     foreach (DataRow row in this.logEntries.Rows) 
     { 
      csvExport.AppendLine(
       string.Format(
       "\"{0}\",\"{1}\",\"{2}\",\"{3}\",\"{4}\",\"{5}\",\"{6}\",\"{7}\",\"{8}\", \"{9}\"", 
       row[ColumnNames.LogTime], row[ColumnNames.Field1], row[ColumnNames.Field2], row[ColumnNames.Field3], row[ColumnNames.Field4], row[ColumnNames.Field5], row[ColumnNames.Field6], row[ColumnNames.Field7], row[ColumnNames.Field8], row[ColumnNames.Field9])); 
     } 

     return csvExport.ToString(); 
    } 

    /// <summary> 
    /// Adds the CSV file to the response. 
    /// </summary> 
    /// <param name="csvExportContents">The contents of the CSV file.</param> 
    internal void DisplayLogFile(string csvExportContents) 
    { 
     byte[] data = new ASCIIEncoding().GetBytes(csvExportContents); 

     HttpContext.Current.Response.Clear(); 
     HttpContext.Current.Response.ContentType = "APPLICATION/OCTET-STREAM"; 
     HttpContext.Current.Response.AppendHeader("Content-Disposition", "attachment; filename=Export.csv"); 
     HttpContext.Current.Response.OutputStream.Write(data, 0, data.Length); 
     HttpContext.Current.Response.End(); 
    } 
+1

Da dove proviene CSVHeader? – RealityDysfunction

+0

Non vedo come i valori che potrebbero contenere '" 'sarebbero sfuggiti. –

+0

@RealityDysfunction Suppongo che provenga da un file resx. Sarebbe più utile per l'esempio avere solo una stringa letterale. – MrBoJangles

4

Si dovrebbe essere in grado di utilizzare gli esempi che utilizzano System.IO.MemoryStream e System.IO.StreamWriter bene.

Invece di scrivere il MemoryStream su un file, lo si dovrebbe restituire come ResponseStream in ASP.NET (assicurandosi di impostare i valori di intestazione appropriati in modo che il browser sappia che sta scaricando un file).

3

Non c'è davvero alcuna magia nella creazione di un file CSV. L'unico problema con i file CSV è che non esiste uno standard e la maggior parte degli importatori implementerà solo un particolare comportamento per il processo di importazione. Se sei fortunato, otterrai qualcosa che può indovinare relativamente bene.

Generare il file CSV è solo una questione di stampa delle linee. Per quanto riguarda i dati reali, familiarizzare con:

http://en.wikipedia.org/wiki/Comma-separated_values

Detto questo, se quello che vuoi è quello di esportare in Excel, è possibile utilizzare Microsoft OOXML SDK:

http://msdn.microsoft.com/en-us/library/bb448854.aspx

Il OOXML SDK è una libreria C# che è possibile utilizzare per generare file Excel sul server. Il blog Brian Jones è una grande risorsa su come usare questa libreria:

http://blogs.msdn.com/b/brian_jones/

+0

Ho bisogno di Excel sul server per questo? – Tom

+0

Non è necessario Excel sul server per eseguire OOXML SDK. In effetti, puoi persino eseguirlo su un sistema Linux con Mono. –

4

Excel è necessaria solo se si utilizza Office Interop. Usando StringWriter, stai semplicemente creando un file e aggiungendo alcune informazioni di risposta. Excel è richiesto solo all'apertura del file.

3

Se non ti dispiace usare una libreria di terze parti e la licenza LGPL va bene nel tuo progetto, allora FileHelpers è un ottimo strumento per questo.

+1

Amo FileHelpers! Questa libreria semplice e veloce semplifica la trasformazione di file flat in oggetti. Può anche trasformare oggetti in file di testo flat in modo orientato agli oggetti. Ed è veloce. E ha un mago per aiutarti a guidarti. Saluti! –

10
private void WriteItem<T>(StreamWriter sr, T item) 
{ 
    string itemString = item.ToString(); 
    if(itemString.IndexOfAny(new char[] { '"', ',', '\n', '\r' }) != -1)//skip test and always escape for different speed/filesize optimisation 
    { 
     sr.Write('"'); 
     sr.Write(itemString.Replace("\"", "\"\"")); 
     sr.Write('"'); 
    } 
    else 
     sr.Write(itemString); 
} 
private void WriteLine<T>(StreamWriter sr, IEnumerable<T> line) 
{ 
    bool first = true; 
    foreach(T item in line) 
    { 
     if(!first) 
      sr.Write(','); 
     first = false; 
     WriteItem(sr, item); 
    } 
} 
private void WriteCSV<T>(StreamWriter sr, IEnumerable<IEnumerable<T>> allLines) 
{ 
    bool first = true; 
    foreach(IEnumerable<T> line in allLines) 
    { 
     if(!first) 
      sr.Write('\n'); 
     first = false; 
     WriteLine(sr, line); 
    } 
} 
private void WriteCSV<T>(HttpResponse response, IEnumerable<IEnumerable<T>> allLines) 
{ 
    response.ContentType = "text/csv"; 
    WriteCSV(response.Output, allLines); 
} 

Può valere anche l'invio di un'intestazione content-disposition con un nome file consigliato.

Modifica: Ultimamente, con i casi in cui è necessario interspazio un'azione tra gli elementi di un'enumerazione (come la virgola e la nuova riga sopra), ho preferito che piuttosto mantenendo un booleano che continua a essere controllato, gestisco l'enumeratore direttamente e quindi gestire il primo elemento separato dal resto. Ho iniziato a fare questo come un micro-opt in un push di efficienza, ma sono cresciuto fino a trovare solo un'espressione migliore di un percorso di codice che differisce per il primo elemento.Come tale, mi piacerebbe ora scrivo quanto sopra come:

private void WriteLine<T>(StreamWriter sr, IEnumerable<T> line) 
{ 
    using(var en = line.GetEnumerator()) 
    if(en.MoveNext()) 
    { 
    WriteItem(sr, en.Current); 
    while(en.MoveNext()) 
    { 
     sr.Write(','); 
     WriteItem(sr, en.Current); 
    } 
} 
private void WriteCSV<T>(StreamWriter sr, IEnumerable<IEnumerable<T>> allLines) 
{ 
    using(var en = allLines.GetEnumerator()) 
    if(en.MoveNext()) 
    { 
     WriteLine(sr, en.Current); 
     while(en.MoveNext()) 
     { 
     sr.Write('\n'); 
     WriteLine(sr, en.Current); 
     } 
    } 
} 
2

Questa è la funzione utilizzata per creare il file CSV:

private async Task<string> WriteCSV<ViewModel>(IEnumerable<ViewModel> viewModels, string path) 
      { 
       Type itemType = typeof(ViewModel); 
       var props = itemType.GetProperties(BindingFlags.Public | BindingFlags.Instance) 
            .OrderBy(p => p.Name); 

       var blobName = string.Empty; 

       using (var ms = new MemoryStream()) 
       { 

        using (var writer = new System.IO.StreamWriter(ms)) 
        { 
         writer.WriteLine(string.Join(", ", props.Select(p => p.Name))); 

         foreach (var item in viewModels) 
         { 

          writer.WriteLine(string.Join(", ", props.Select(p => p.GetValue(item, null)))); 
         } 
    } 

} 
    } 

Altrimenti si può fare riferimento sotto il collegamento:

https://travelcodingnlotsmore.wordpress.com/2013/06/06/creating-csv-file-from-data-in-list-object-in-c/