Генерация CSV из ASP.NET MVC приложения
Интересный метод генерации CSV из ASP.NET MVC с помощью класса-наследника System.Web.Mvc.FileResult. Плюсы решения:
- Используется стандартный класс ASP.NET MVC для генерации файла для скачивания
- CSV можно сгенерировать практически из любой коллекции объектов
- Заголовки колонок генерируются из значений атрибутов
[DisplayName] - Можно указать имена свойств, которые не нужно экспортировать
Похоже, в оригинальном коде есть ошибка — исключение полей из набора не работало как нужно. К сожалению, комментарии к оригинальной статье закрыты, исправленный вариант кода ниже.
Если класс располагается не в ASP.NET MVC проекте (а, например, в проекте типа Class Library) для корректной компиляции важно не забыть добавить в References System.Web.Abstractions.
/*
Author:
http://www.notesfor.net/post/2010/06/28/AspNet-custom-ActionResult-CSV.aspx
*/
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Web;
using System.IO;
using System.Web.Mvc;
namespace NotesForNet.CSV
{
public sealed class CSVResult<T> : System.Web.Mvc.FileResult
{
private IEnumerable<T> collection;
private String[] columnsToExclude;
public CSVResult(IEnumerable<T> collection, params String[] columnsToExclude)
: base("text/csv")
{
this.collection = collection;
this.columnsToExclude = columnsToExclude;
}
protected override void WriteFile(HttpResponseBase response)
{
System.IO.Stream outputStream = response.OutputStream;
using (MemoryStream mstream = new MemoryStream())
{
WriteObject(mstream);
outputStream.Write(mstream.GetBuffer(), 0, (int)mstream.Length);
}
}
private void WriteObject(Stream stream)
{
// We will follow the recommandations stated in this article
// http://www.commentcamarche.net/faq/sujet-7273-exporter-a-coup-sur-du-csv
// - embedding all values in quotes
// - double quote literal values
// - add \n\r between each lines.
StreamWriter writer = new StreamWriter(stream, System.Text.Encoding.Default);
// Render columns
Type modelType = typeof(T);
List<ModelMetadata> metadatas = ModelMetadataProviders.Current.GetMetadataForProperties(null, modelType).ToList();
for (int i = 0; i < metadatas.Count; )
{
if (Array.IndexOf<String>(columnsToExclude, metadatas[i].PropertyName) != -1)
metadatas.RemoveAt(i);
else
{
WriteValue(writer, metadatas[i].DisplayName ?? metadatas[i].PropertyName);
i++;
}
}
writer.WriteLine();
// Render data
var en = collection.GetEnumerator();
// Start of fix
while (en.MoveNext())
{
ModelMetadata mprop = ModelMetadataProviders.Current.GetMetadataForType(() => en.Current, modelType);
foreach (ModelMetadata prop in mprop.Properties)
{
if (Array.IndexOf<String>(columnsToExclude, prop.PropertyName) == -1)
{
WriteValue(writer, prop.SimpleDisplayText ?? String.Empty);
}
}
writer.WriteLine();
}
// End of fix
writer.Flush();
}
private static void WriteValue(StreamWriter writer, String literal)
{
// Enclose values in quote
writer.Write("\"");
string line = literal.Replace("\"", "\"\"");
writer.Write(line);
writer.Write("\";");
}
}
}
