Web API HttpResponseMessage content return is incomplete - c#

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

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.

Related

iText 7 return Pdf from Asp.Net WebApi

So I am alreay searching about this the entire day and I just don't know how to get it working. Basically I want to create a PDF server side with iText 7 in my ASP.Net WebApi. Very straightforward and easy Pdf creation:
[HttpGet]
public HttpResponseMessage CreateLieferschein()
{
MemoryStream stream = new MemoryStream();
PdfWriter writer = new PdfWriter(stream);
var pdf = new PdfDocument(writer);
var document = new Document(pdf);
document.Add(new Paragraph("Hello World!"));
}
From here on I have no idea on how to return the file from the controller. I would really appreciate any help since I am just lost.
I haven't tried this out, just doing it freehand so bear with me, but I think you can get the idea of what's going on here.
public HttpResponseMessage CreateLieferschein() {
// Create the itext pdf
MemoryStream stream = new MemoryStream();
PdfWriter writer = new PdfWriter(stream);
var pdf = new PdfDocument(writer);
var document = new Document(pdf);
document.Add(new Paragraph("Hello World!"));
document.Close(); // don't forget to close or the doc will be corrupt! ;)
// Load the mem stream into a StreamContent
HttpResponseMessage httpResponseMessage = new HttpResponseMessage(HttpStatusCode.OK)
{
Content = new StreamContent(stream)
};
// Prep the response with headers, filenames, etc.
httpResponseMessage.Content.Headers.ContentDisposition = new ContentDispositionHeaderValue("attachment")
{
FileName = "WebApi2GeneratedFile.pdf"
};
httpResponseMessage.Content.Headers.ContentType = new MediaTypeHeaderValue("application/pdf");
ResponseMessageResult responseMessageResult = ResponseMessage(httpResponseMessage);
// Cross your fingers...
return responseMessageResult;
}
try this :
[HttpGet]
public HttpResponseMessage CreateLieferschein()
{
Document doc = new Document(iTextSharp.text.PageSize.LETTER, 10, 10, 42, 35);
byte[] buffer;
HttpResponseMessage response = Request.CreateResponse(HttpStatusCode.BadRequest);
using(MemoryStream stream = new MemoryStream())
{
using(PdfWriter wri = PdfWriter.GetInstance(doc, mem))
{
PdfWriter writer = new PdfWriter(stream);
var pdf = new PdfDocument(writer);
var document = new Document(pdf);
document.Add(new Paragraph("Hello World!"));
}
buffer = stream.ToArray();
var contentLength = buffer.Length;
var statuscode = HttpStatusCode.OK;
response = Request.CreateResponse(statuscode);
response.Content = new StreamContent(new MemoryStream(buffer));
response.Content.Headers.ContentType = new MediaTypeHeaderValue("application/pdf");
response.Content.Headers.ContentLength = contentLength;
ContentDispositionHeaderValue contentDisposition = null;
if (ContentDispositionHeaderValue.TryParse("inline; filename=" + document.Name + ".pdf", out contentDisposition)) {
response.Content.Headers.ContentDisposition = contentDisposition;
}
} else {
var statuscode = HttpStatusCode.NotFound;
var message = String.Format("Unable to find file. file \"{0}\" may not exist.", docid);
var responseData = responseDataFactory.CreateWithOnlyMetadata(statuscode, message);
response = Request.CreateResponse((HttpStatusCode)responseData.meta.code, responseData);
}
return response;
}

Convert LINQ result to CSV or Excel

Using a LINQ query I need to export to Excel when a WebApi method is called. I have built the LINQ query that will return the correct data, now I need it to export to .csv or Excel file format.
I have tried using MemoryStream and StreamWriter but I think I am just chasing my tail now.
[HttpGet]
[Route("Download")]
public Task<IActionResult> Download(int memberId)
{
var results = (from violations in _db.tblMappViolations
where violations.MemberID == memberId
select new IncomingViolations
{
Contact = violations.ContactName,
Address = violations.str_Address,
City = violations.str_City,
State = violations.str_State,
Zip = violations.str_Zipcode,
Country = violations.str_Country,
Phone = violations.str_Phone,
Email = violations.str_Email,
Website = violations.str_WebSite,
}).FirstOrDefault();
MemoryStream stream = new MemoryStream(results);
StreamWriter writer = new StreamWriter(stream);
writer.Flush();
stream.Position = 0;
FileStreamResult response = File(stream, "application/octet-stream");
response.FileDownloadName = "violations.csv";
return response;
}
Here is how you can send CSV file to the user from server.
string attachment = "attachment; filename=MyCsvLol.csv";
HttpContext.Current.Response.Clear();
HttpContext.Current.Response.ClearHeaders();
HttpContext.Current.Response.ClearContent();
HttpContext.Current.Response.AddHeader("content-disposition", attachment);
HttpContext.Current.Response.ContentType = "text/csv";
HttpContext.Current.Response.AddHeader("Pragma", "public");
var sb = new StringBuilder();
// Add your data into stringbuilder
sb.Append(results.Contact);
sb.Append(results.Address);
sb.Append(results.City);
// and so on
HttpContext.Current.Response.Write(sb.ToString());
For Sending it from API
MemoryStream stream = new MemoryStream();
StreamWriter writer = new StreamWriter(stream);
// Write Your data here in writer
writer.Write("Hello, World!");
writer.Flush();
stream.Position = 0;
HttpResponseMessage result = new HttpResponseMessage(HttpStatusCode.OK);
result.Content = new StreamContent(stream);
result.Content.Headers.ContentType = new MediaTypeHeaderValue("text/csv");
result.Content.Headers.ContentDisposition = new ContentDispositionHeaderValue("attachment") { FileName = "Export.csv" };
return result;
Update:-
public HttpResponseMessage Download()
{
var results = (from violations in _db.tblMappViolations
where violations.MemberID == memberId
select new IncomingViolations
{
Contact = violations.ContactName,
Address = violations.str_Address,
City = violations.str_City,
State = violations.str_State,
Zip = violations.str_Zipcode,
Country = violations.str_Country,
Phone = violations.str_Phone,
Email = violations.str_Email,
Website = violations.str_WebSite,
});
var sb = new StringBuilder();
MemoryStream stream = new MemoryStream();
StreamWriter writer = new StreamWriter(stream);
foreach(var tempResult in results)
{
sb.Append(tempResult.Contact+",");
sb.Append(tempResult.Address+",");
sb.Append(tempResult.City+",");
sb.Append(tempResult.State+",");
sb.Append(tempResult.Zip+",");
sb.Append(tempResult.Country+",");
sb.Append(tempResult.Phone+",");
sb.Append(tempResult.Email+",");
sb.Append(tempResult.Website+",");
sb.Append(Enviroment.NewLine);
}
writer.Write(sb.ToString());
writer.Flush();
stream.Position = 0;
HttpResponseMessage result = new HttpResponseMessage(HttpStatusCode.OK);
result.Content = new StreamContent(stream);
result.Content.Headers.ContentType = new MediaTypeHeaderValue("text/csv");
result.Content.Headers.ContentDisposition = new ContentDispositionHeaderValue("attachment") { FileName = "Export.csv" };
return result;
}
First, to reuse the code in other areas, always create helper classes.
I adopted this method of converting list into a stream with headers as property names, if you want a file from this, essentially, I would just add another step to this:
STEP 1:
public static Stream ConvertToCSVStream<T>(IEnumerable<T> objects)
{
Type itemType = typeof(T);
var properties = itemType.GetProperties();
var mStream = new MemoryStream();
StreamWriter sWriter = new StreamWriter(mStream);
var values = objects.Select(o =>
{
return string.Join(",", properties.Select(p =>
{
var value = p.GetValue(o).ToString();
if (!Regex.IsMatch(value, "[,\"\\r\\n]"))
{
return value;
}
value = value.Replace("\"", "\"\"");
return string.Format("\"{0}\"", value);
})) + sWriter.NewLine;
});
var valuesInStrings = values.Aggregate((current, next) => current + next);
try
{
sWriter.Write(string.Join(",", properties.Select(x => x.Name.Replace("_", " "))) + sWriter.NewLine);
sWriter.Write(valuesInStrings);
}
catch (Exception e)
{
mStream.Close();
throw e;
}
sWriter.Flush();
mStream.Position = 0;
return mStream;
}
if your data is text, just convert it directly to a file result but if not, you must convert it to binary array and write it to stream, refer to this article for converting it to binary data, in our case, for csv, you could just use the FileStream result that you've implemented in a separate method:
STEP 2:
public FileStreamResult CreateFile(MemoryStream mStream, string path, string name)
{
//set values, names, content type, etc
//return filestream
}
or any other method you find better.
Save your result in a DataTable and then just use this
XLWorkbook workbook = new XLWorkbook();
DataTable table = GetYourTable();
workbook.Worksheets.Add(table);
And you should definitely use stream writer for this if you know which file its going to write to from the start, else stream reader and then stream writer.

How to convert GZipStream to HttpContent?

I have written a Web API code to return a zip file. But I am not able to convert the GZipStream content to HttpContent. I get the following error:
cannot implicitly convert type 'system.io.compression.GZipStream' to
'System.Net.Http.HttpContent'
Where did I go wrong?
My WebApi Code:
var content =
new GZipStream(memStream, CompressionMode.Compress);
HttpResponseMessage httpResponseMessage = new HttpResponseMessage();
httpResponseMessage.Content = content;
httpResponseMessage.Content.Headers.Add("x-filename", document.FileName);
httpResponseMessage.Content.Headers.ContentType = new MediaTypeHeaderValue("application/zip");
httpResponseMessage.Content.Headers.ContentDisposition = new ContentDispositionHeaderValue("attachment");
httpResponseMessage.Content.Headers.ContentDisposition.FileName = "xyz.zip";
httpResponseMessage.StatusCode = HttpStatusCode.OK;
I got a solution using Zip archive and I have created a static class to push the memory stream into a zip archive as below,
var pushStreamContent = ZipStreamContent.Create("MultipleDocument.zip", memStList);
ZipStreamContent class,
public static class ZipStreamContent
{
public static PushStreamContent Create(string fileName, List<MemoryStream> msList)
{
var content = new PushStreamContent((outputStream, httpContent, transportContext) =>
{
using (var zip = new ZipArchive(outputStream, ZipArchiveMode.Create, leaveOpen: false))
{
msList[0].Position = 0;
var createenter = zip.CreateEntry("xyz.jpg", CompressionLevel.Optimal);
using (var s = createenter.Open())
{
msList[0].CopyTo(s);
}
}
});
content.Headers.ContentType = new MediaTypeHeaderValue("application/octet-stream");
content.Headers.ContentDisposition = new ContentDispositionHeaderValue("attachment");
content.Headers.ContentDisposition.FileName = fileName;
return content;
}
}
I have simplified the above given GipStreamContent static class like following, It's working well, so I hope It will help all others.
CloudBlockBlob blob = null;
//azure storage connection
var container = GetBlobClient(tenantInfo);
//directory reference
var directory = container.GetDirectoryReference(
string.Format(DirectoryNameConfigValue, tenantInfo.TenantId.ToString(), documentList[0].ProjectId));
var pushStreamContent = new PushStreamContent(async (outputStream, httpContent, transportContext) =>
{
//zip the multiple files
using (var zipEntry = new ZipArchive(outputStream, ZipArchiveMode.Create, leaveOpen: false))
{
for (int docId = 0; docId < documentList.Count; docId++)
{
blob = directory.GetBlockBlobReference(DocumentNameConfigValue + documentList[docId].DocumentId);
if (!blob.Exists()) continue;
MemoryStream memStream = new MemoryStream();
await blob.DownloadToStreamAsync(memStream);
memStream.Position = 0;
var createEntry = zipEntry.CreateEntry(documentList[docId].FileName, CompressionLevel.Fastest);
using (var stream = createEntry.Open())
{
memStream.CopyTo(stream);
}
}
}
});

How to generate a zip file within the HttpResponseMessage from within an Api Controller [duplicate]

I have a web service that I can call and save the returned csv file. Everything seems to be working OK. What I am now interested in doing is returning multiple CSV files for the user to download. What is the proper way to handle this? I'm guessing I need a way to package them up (zip? perhaps)?
[HttpPost]
[Route("OutputTemplate")]
public HttpResponseMessage OutputTemplate()
{
HttpResponseMessage msg = new HttpResponseMessage();
string body = this.Request.Content.ReadAsStringAsync().Result;
try
{
string contents = DoStuff(body) // get contents based on body
MemoryStream stream = new MemoryStream();
StreamWriter writer = new StreamWriter(stream);
writer.Write(contents);
writer.Flush();
stream.Position = 0;
msg.StatusCode = HttpStatusCode.OK;
msg.Content = new StreamContent(stream);
msg.Content.Headers.ContentType = new MediaTypeHeaderValue("text/csv");
msg.Content.Headers.ContentDisposition = new ContentDispositionHeaderValue("attachment")
{
FileName = "fileexport"
};
return msg;
}
...
}
Using the following model to abstract file name and content
public class FileModel {
public string FileName { get; set; }
public byte[] FileContent { get; set; }
}
The following extension was derived to compress the file content
public static class ZipArchiveExtensions {
public static Stream Compress(this IEnumerable<FileModel> files) {
if (files.Any()) {
var ms = new MemoryStream();
using(var archive = new ZipArchive(
stream: ms,
mode: ZipArchiveMode.Create,
leaveOpen: true
)){
foreach (var file in files) {
var entry = archive.add(file);
}
}
ms.Position = 0;
return ms;
}
return null;
}
private static ZipArchiveEntry add(this ZipArchive archive, FileModel file) {
var entry = archive.CreateEntry(file.FileName, CompressionLevel.Fastest);
using (var stream = entry.Open()) {
stream.Write(file.FileContent, 0, file.FileContent.Length);
}
return entry;
}
}
With that in place, the example API controller action could look something like this.
public class ExampleApiController : ApiController {
public async Task<IHttpActionResult> OutputTemplate() {
IHttpActionResult result = BadRequest();
var body = await Request.Content.ReadAsStreamAsync();
List<FileModel> files = DoSomething(body);
if (files.Count > 1) {
//compress the files.
var archiveStream = files.Compress();
var content = new StreamContent(archiveStream);
var response = Request.CreateResponse(System.Net.HttpStatusCode.OK);
response.Content = content;
response.Content.Headers.ContentType = new MediaTypeHeaderValue("application/zip");
response.Content.Headers.ContentDisposition = new ContentDispositionHeaderValue("attachment") {
FileName = "fileexport.zip"
};
result = ResponseMessage(response);
} else if (files.Count == 1) {
//return the single file
var fileName = files[0].FileName; //"fileexport.csv"
var content = new ByteArrayContent(files[0].FileContent);
var response = Request.CreateResponse(System.Net.HttpStatusCode.OK);
response.Content = content;
response.Content.Headers.ContentType = new MediaTypeHeaderValue("text/csv");
response.Content.Headers.ContentDisposition = new ContentDispositionHeaderValue("attachment") {
FileName = fileName
};
result = ResponseMessage(response);
}
return result;
}
private List<FileModel> DoSomething(System.IO.Stream body) {
//...TODO: implement file models
throw new NotImplementedException();
}
}

Return Zip file as Response in Web Api

Hi I have created a web api application and I am returning response as text file. Now I want to return a zipped file so that its size is reduced
This is my controller code
public HttpResponseMessage Get(String parameter)
{
var response = new HttpResponseMessage(HttpStatusCode.OK);
response.Content = new StringContent(informationcontext.Records(parameter));
response.Content.Headers.ContentType = new MediaTypeHeaderValue("text/plain");
response.Content.Headers.ContentDisposition = new ContentDispositionHeaderValue("attachment")
{
FileName = "sample"
};
return response;
}
Can any one help me in this?
public HttpResponseMessage Post()
{
var ch = new ClientHandler();
using (MemoryStream rms = new MemoryStream(Request.Content.ReadAsByteArrayAsync().Result))
{
using (GZipStream unzip = new GZipStream(rms, CompressionMode.Decompress))
{
ch.Requests = XElement.Load(unzip);
}
}
MemoryStream outstream = new MemoryStream();
try
{
ch.Start();
using (GZipStream zip = new GZipStream(outstream, CompressionMode.Compress, true))
{
ch.Responses.Save(zip);
}
}
catch (Exception ex)
{
XElement responses = new XElement("Responses");
responses.Add(new XElement("Error", ex.Message));
using (GZipStream zip = new GZipStream(outstream, CompressionMode.Compress, true))
{
responses.Save(zip);
}
}
finally
{
ch.Dispose();
}
outstream.Position = 0;
HttpResponseMessage result = new HttpResponseMessage(HttpStatusCode.OK);
result.Content = new StreamContent(outstream);
result.Content.Headers.ContentType = new MediaTypeHeaderValue("application/octet-stream");
return result;
}

Categories