Using CSVHelper to output stream to browser but unable to save file - c#

I am trying to achieve exactly what the poster in this question is trying to do except the browser (Chrome) doesn't attempt to download the .csv file or present the user with a download box. I am using a HTTP GET request and the code is virtually identical in the question above. I'm using Fiddler to monitor the traffic and I can see the .csv file stream in the response, but the browser seems to be ignore it and I can't work out why...
Here is my current implemenation (base on #MattThrower's question):
I make an AJAX call to the MVC controller:
$("#exportToCsvLink").click(function () {
$.ajax({
type: "GET",
url: window.project.common.routes.urls.ExportChartDataToCsv,
data: { parameterId: parameter.parameterId }
});
});
The MVC controller processes the CSV export and returns a FileStreamResult
public FileStreamResult ExportChartDataToCsv(int parameterId)
{
List<TestViewModel> data = _CommonService.GetData(parameterId);
var result = WriteCsvToMemory(data);
var memoryStream = new MemoryStream(result);
return new FileStreamResult(memoryStream, "text/csv") { FileDownloadName = "export.csv" };
}
public byte[] WriteCsvToMemory(IEnumerable<TestViewModel> data)
{
using (var memoryStream = new MemoryStream())
using (var streamWriter = new StreamWriter(memoryStream))
using (var csvWriter = new CsvWriter(streamWriter))
{
csvWriter.WriteRecords(data);
streamWriter.Flush();
return memoryStream.ToArray();
}
}

Related

Download XML file with Asp.Net MVC

I've created an ActionResult in an MVC controller which returns a FileStreamResult of an XMLfile, but it's not downloading the file.
After the action returns the FileStream nothing happens; I'd like to make the user download the file from the web application.
Any help?
Thanks
public ActionResult EsportaProtocollo(int idProtocollo)
{
AvvisoModel avviso = new AvvisoModel();
string xml = _protocolliService.ProtocolloToXml(idProtocollo);
var newStream = new System.IO.MemoryStream();
var writer = new System.IO.StreamWriter(newStream);
writer.Write(xml);
writer.Flush();
newStream.Position = 0;
return File(newStream, "application/xml", "prova.xml");
}
Here's what I see on Fiddler
Solved! the problem was the javascript, i was calling the action with an ajax call, i've changed it to a window.open(url); and now it works!

Tweetsharp SendTweetWithMedia from url

I am trying to use Tweetsharp's SendTweetWithMedia with a image which I don't have stored locally, I only have a url. All the examples I have found of SendTweetWithMedia use a file on the local system.
var thumb = "http://somesite.net/imageurl";
var service = new TwitterService(key, secret);
service.AuthenticateWith(token, tokenSecret);
var req = WebRequest.Create(thumb);
using (var stream = req.GetResponse().GetResponseStream())
{
response = service.SendTweetWithMedia(new SendTweetWithMediaOptions
{
Status = tweet.Trim(),
Images = new Dictionary<string, Stream> { { fullname, stream } }
});
}
I get the following error from SendTweetWithMedia:
'System.NotSupportedException': This stream does not support seek operations.
I could download the file from the url and save locally, but I'd rather use the url. Is this possible?
In the end, I just created a temporary file:
byte[] data;
using (var client = new WebClient())
{
data = client.DownloadData(thumb);
}
File.WriteAllBytes($"{Path.GetTempPath()}\\xyz.jpg", data);
best answer I could come up with. Still a few more lines than I'd like though.

Failed to load PDF document in web api

I am getting error while downloading and opening pdf files as
Failed to load PDF document
. But when i tried to download txt file it gets download success fully. I need to this ajax request to be as POST method, So after searching in internet i found this code.
$.ajax({
type: "POST",
url: url,
cache: false,
contentType: false,
processData: false,
success: function (data) {
var blob = new Blob([data]);
var link = document.createElement('a');
link.href = window.URL.createObjectURL(blob);
link.download = textName;
link.click();
}, error: function (data) { alert("error"); }});
I use web api for downloading the file
public HttpResponseMessage Download(string fileName)
{
string filePath = Path.Combine(PATH, fileName.Replace('/', '\\'));
byte[] pdf = System.IO.File.ReadAllBytes(filePath);
HttpResponseMessage result = Request.CreateResponse(HttpStatusCode.OK);
result.Content = new ByteArrayContent(pdf);
result.Content.Headers.ContentDisposition = new ContentDispositionHeaderValue("attachment");
result.Content.Headers.ContentDisposition.FileName = "MyPdf.pdf";
result.Content.Headers.ContentType = new MediaTypeHeaderValue("application/pdf");
return result;
}
Please help me
Referencing Download pdf file using jquery ajax
jQuery has some issues loading binary data using AJAX requests, as it
does not yet implement some HTML5 XHR v2 capabilities
It provides two options for you try. I won't repeat them here as they belong to that answer. Check the link.
On the server side, I've had success downloading PDF files using this method.
[HttpPost]
public HttpResponseMessage Download(string fileName)
{
string filePath = Path.Combine(PATH, fileName.Replace('/', '\\'));
byte[] pdf = System.IO.File.ReadAllBytes(filePath);
//content length for header
var contentLength = pdf.Length;
var statuscode = HttpStatusCode.OK;
var result = Request.CreateResponse(statuscode);
result.Content = new StreamContent(new MemoryStream(buffer));
result.Content.Headers.ContentType = new MediaTypeHeaderValue("application/pdf");
result.Content.Headers.ContentLength = contentLength;
result.Content.Headers.ContentDisposition = new ContentDispositionHeaderValue("attachment");
result.Content.Headers.ContentDisposition.FileName = "MyPdf.pdf";
return result;
}
Not that much different to your original.
You should also check to make sure that the original file was not corrupt on the server if you are reading from disk/database or if you are dynamically generating the file.
The problem is probably your Content-Disposition header. You need inline, but you have attachment.
result.Content.Headers.ContentDisposition = new ContentDispositionHeaderValue("inline");
What you have now tells the browser to specifically send the file directly, and never attempt to display it in-browser. Chrome's internal PDF viewer currently chokes when you do this.
More: Content-Disposition:What are the differences between "inline" and "attachment"?

How upload csv file and parse it?

I need upload csv file and parse it.
I can see file in http body request, but when I pass it in the csvreader I can see base64 string like headers:
On client side I use angularjs:
'uploadBulkUsersFile': {
method: 'POST', url: CONFIG.apiServiceBaseUri + "api/users/bulkUsers",
headers: {
"Content-Type": undefined
},
transformRequest: angular.identity,
withCredentials: true
},
and call:
var _uploadBulkUsersFile = function (bulkUsersFile) {
var fd = new FormData();
fd.append("file", bulkUsersFile);
return usersResource.uploadBulkUsersFile({}, fd, function (result) {
return result;
}).$promise;
};
and on server side I use webapi2:
if (!Request.Content.IsMimeMultipartContent())
throw new HttpResponseException(HttpStatusCode.UnsupportedMediaType);
var provider = new MultipartMemoryStreamProvider();
await Request.Content.ReadAsMultipartAsync(provider);
foreach (var file in provider.Contents)
{
var buffer = await file.ReadAsStreamAsync();
TextReader textReader = new StreamReader(buffer);
var csv = new CsvReader(textReader);
var records = csv.GetRecords<BulkUploadUser>().ToList();
return Created("DefaultApi", records);
}
http request payload
------WebKitFormBoundarySKPlgJRINOMnpxVP
Content-Disposition: form-data; name="file"
data:application/vnd.ms-excel;base64,RW1haWwsRmlyc3ROYW1lLExhc3ROYW1lDQpwcm9zdG8uZHVkYUBnbWFpbC5jb20yLERlbmlzLER1ZGFpZXYNCnByb3N0by5kdWRhQGdtYWlsLmNvbSxEZW5pcyxEdWRhaWV2DQpwcm9zdG8uZHVkYUBnbWFpbC5jb20yLERlbmlzLER1ZGFpZXYNCg==
------WebKitFormBoundarySKPlgJRINOMnpxVP--
UPDATE
#Ubercode suggest me convert base 64 to string, I made it, but is looks very disgusting:
var buffer = await file.ReadAsStreamAsync();
TextReader textReader = new StreamReader(buffer);
var text = textReader.ReadToEnd();
var indexOfWord = "base64,";
var base64EncodedBytes = System.Convert.FromBase64String(text.Substring(text.IndexOf(indexOfWord) + indexOfWord.Length));
var encoded = System.Text.Encoding.UTF8.GetString(base64EncodedBytes);
TextReader textReader2 = new StringReader(encoded);
var csv = new CsvReader(textReader2);
var records = csv.GetRecords<BulkUploadUser>().ToList();
You need to decode the base64 encoded stuff into your file:
How do I encode and decode a base 64 string
You can tidy your code up a little bit thus:
string text;
using(TextReader textReader = new StreamReader(buffer))
text = textReader.ReadToEnd();
CsvReader csv;
using(var ms
= new MemoryStream(Convert.FromBase64String(text.Substring(text.IndexOf(',') + 1)))
using (var textReader2 = new StreamReader(ms))
csv = new CsvReader(textReader2);
var records = csv.GetRecords<BulkUploadUser>().ToList();
if (!Request.Content.IsMimeMultipartContent())
{
return StatusCode(HttpStatusCode.UnsupportedMediaType);
}
HttpPostedFile uploadedFile = HttpContext.Current.Request.Files[0];
var csv = new CsvReader(uploadedFile.InputStream);
The uploaded data is indeed csv, not excel:
Email,FirstName,LastName
prosto.duda#gmail.com2,Denis,Dudaiev
prosto.duda#gmail.com,Denis,Dudaiev
prosto.duda#gmail.com2,Denis,Dudaiev
It would be best to prevent the data being converted to base64 in the first place (it's bigger and needs decoding).
For some reason, the data is send as 'excel' and in base64:
data:application/vnd.ms-excel;base64,
So you probably want to check the following:
headers: {
"Content-Type": undefined
},
transformRequest: angular.identity,
I don't know AngularJS, but you might want to change Content-Type to text/csv and check transformRequest.
Or maybe it gets converted to base64 here:
var fd = new FormData();
fd.append("file", bulkUsersFile);
So it would be best to check where the data gets converted and fix it to send plain text instead. This will prevent the encoding/decoding step, and the data will be smaller too.
Just had the same problem, thanks for the advices in this post.
First of all, there are two possibilities to upload a file: Http-POST-Content or form-data (is post too, but not equally the same).
I'm using the simpler method, the Http-POST-Content. There are no problems with base64 encoding, etc, the solution stays simple, but I think it can be pretty uneficcent with huge files.
I used the follwing code in the angular Project:
Angular Code
Import Dialog (html):
<input hidden #imageInput type="file" accept=".csv" (change)="processFile(imageInput)">
<button mat-button class="image-upload-button full-width" (click)="imageInput.click()">
<mat-icon>add</mat-icon>
</button>
Dialog Code (.ts)
const file: File = imageInput.files[0];
this.dialogRef.close(file);
ts-Code executed after dialog close
const url = 'myapi/Import';
return this.http.post<ImportResult>(url, file).toPromise();
C# Code
try
{
var result = new ImportResult();
s = System.Web.HttpContext.Current.Request.GetBufferedInputStream();
using (var sr = new System.IO.StreamReader(s))
{
s = null;
var reader = new CsvReader(sr, new CsvConfiguration
{
Delimiter = ";"
});
var records = reader.GetRecords<RowEntry>();
...
}
return result;
}
finally
{
s?.Dispose();
}
Hope this will help, it's a simple solution working fine.

ASP MVC Image upload error when using HttpClient.PostySync

I have asp mvc web page to upload image, I need to validate the image width and height. I try to convert image from FromStream and than post it to server via PostSync method. I do not get any error but image is not posting to the server. If I bypass the FromStream method, than I do not see any error
public virtual ActionResult SaveFileConfigure(ConfigurationDto configuration, HttpPostedFileBase filePost)
{
System.IO.Stream stream = filePost.InputStream;
System.Drawing.Image image = System.Drawing.Image.FromStream(stream);
//check image width here
WebApiClient.UploadFile(this.FileService, stream, configuration.FileName);
}
Here is web api upload code
public static void UploadFile(string serviceUrl, Stream file, string fileName)
{
using (var client = new HttpClient())
{
using (var content = new MultipartFormDataContent())
{
var fileContent = new StreamContent(file);
fileContent.Headers.ContentDisposition = new ContentDispositionHeaderValue("attachment") {
FileName = fileName
};
content.Add(fileContent);
var result = client.PostAsync(string.Format("{0}/upload", serviceUrl.TrimEnd('/')), content).Result;
}
}
}
I am able to fix the issue by using FromFile method.
System.Drawing.Image image = System.Drawing.Image.FromFile(filePost.FileName);
Looks like after I use FromStream method, the stream is getting closed and not able to post the file.

Categories