Reputation: 418
I would like to return a csv generated from my database using Web API 5.0 It is working fine except that the csv returned is truncated.
I assume the issue is on the MemoryBuffer management, but I can't find where it is.
My code (solved):
IEnumerable<MasterObsTrip> masterTripList = _obsvMasterRepo.GetObsTrips(vesselName, dateYear, port, obsvCode, obsvTripCode, obsvProgCode, lastModifiedDateYear, lastModifiedBy, statusCode);
IList<MasterObsTripModel> masterTripModelList = new List<MasterObsTripModel>();
foreach (MasterObsTrip trip in masterTripList)
masterTripModelList.Add(new MasterObsTripModel(trip));
var stream = new MemoryStream();
var writer = new StreamWriter(stream);
CsvFileDescription outputFileDescription = new CsvFileDescription
{
SeparatorChar = ',', // comma delimited
FirstLineHasColumnNames = true, // no column names in first record
FileCultureName = "nl-NL" // use formats used in The Netherlands
};
CsvContext cc = new CsvContext();
cc.Write(masterTripModelList,writer,outputFileDescription);
writer.Flush();
stream.Position = 0;
HttpResponseMessage response = new HttpResponseMessage(HttpStatusCode.OK);
response.Content = new StreamContent(stream);
response.Content.Headers.ContentType = new MediaTypeHeaderValue("application/octet-stream");
response.Content.Headers.ContentDisposition = new ContentDispositionHeaderValue("attachment");
response.Content.Headers.ContentDisposition.FileName = "ObserverTripList.csv";
stream.Flush();
return response;
Thanks
Upvotes: 5
Views: 8875
Reputation: 142014
I would try doing writer.Flush() just before you reset the stream.Position = 0
Also, if you often have a need for CSV content I would also suggest creating yourself a CsvContent class.
public class CsvContent<T> : HttpContent
{
private readonly MemoryStream _stream = new MemoryStream();
public CsvContent(CsvFileDescription outputFileDescription, string filename, IEnumerable<T> data)
{
var cc = new CsvContext();
var writer = new StreamWriter(_stream);
cc.Write(data, writer, outputFileDescription);
writer.Flush();
_stream.Position = 0;
Headers.ContentType = new MediaTypeHeaderValue("application/octet-stream");
Headers.ContentDisposition = new ContentDispositionHeaderValue("attachment");
Headers.ContentDisposition.FileName = filename;
}
protected override Task SerializeToStreamAsync(Stream stream, TransportContext context)
{
return _stream.CopyToAsync(stream);
}
protected override bool TryComputeLength(out long length)
{
length = _stream.Length;
return true;
}
}
Then your controller action reduces to...
IEnumerable<MasterObsTrip> masterTripList = _obsvMasterRepo.GetObsTrips(vesselName, dateYear, port, obsvCode, obsvTripCode, obsvProgCode, lastModifiedDateYear, lastModifiedBy, statusCode);
IList<MasterObsTripModel> masterTripModelList = new List<MasterObsTripModel>();
foreach (MasterObsTrip trip in masterTripList)
masterTripModelList.Add(new MasterObsTripModel(trip));
CsvFileDescription outputFileDescription = new CsvFileDescription
{
SeparatorChar = ',', // comma delimited
FirstLineHasColumnNames = true, // no column names in first record
FileCultureName = "nl-NL" // use formats used in The Netherlands
};
HttpResponseMessage response = new HttpResponseMessage(HttpStatusCode.OK) {
Content = new CsvContent<MasterObsTripModel> (outputFileDescription,
"ObserverTripList.csv",
masterTripModelList);
}
return response;
This could be simplified further by including the creation of the CsvFileDescription inside the CsvContent class.
Upvotes: 5