Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Web API HttpResponseMessage content return is incomplete

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

like image 554
Bruno Deprez Avatar asked Nov 13 '13 22:11

Bruno Deprez


1 Answers

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.

like image 56
Darrel Miller Avatar answered Nov 15 '22 03:11

Darrel Miller