Kendo UI Async Upload not working in Internet Explorer - asp.net-mvc

I'm trying to use the Kendo UI Upload (MVC wrapper) in async mode. Things seem to work fine in Chrome, but no such luck in IE (as of now only tested in IE 9). When it initiates the upload, I can see it hitting my action method and the request contains the data I expect, but nothing is actually being saved.
Code samples are below:
_EditForm.cshtml (where the upload is)
#(Html.Kendo().Upload()
.Name(string.Format("upload{0}", "background"))
.Multiple(true)
.Events(evt => evt.Success("refreshBackgroundImages"))
.Messages(msg => msg.DropFilesHere("drag and drop images from your computer here")
.StatusUploaded("Files have been uploaded"))
.Async(a => a.AutoUpload(true)
.SaveField("files")
.Save("UploadImage", "Packages", new { siteId = Model.WebsiteId, type = "background" })))
Controller ActionMethod
[HttpPost]
public ActionResult UploadImage(IEnumerable<HttpPostedFileBase> files, Guid siteId, string type)
{
var site = _websiteService.GetWebsite(siteId);
var path = Path.Combine(_fileSystem.OutletVirtualPath, site.Outlet.AssetBaseFolder);
if (type == "background")
{
path = Path.Combine(path, _backgroundImageFolder);
}
else if (type == "image")
{
path = Path.Combine(path, _foregroundImageFolder);
}
foreach (var file in files)
{
_fileSystem.SaveFile(path, file.FileName, file.InputStream, file.ContentType, true);
}
// Return empty string to signify success
return Content("");
}

Well as another post said, "Welcome to episode 52,245,315 of 'Why Does Internet Explorer suck so badly':
Turns out that when you do file.FileName on an HttpPostedFileBase in Internet Explorer, it thinks you want the whole path of the file on the local machine. It's obviously an IE only thing as Chrome and Firefox seem to have it right.
Make sure to do the following when you only want the actual FileName:
var filename = Path.GetFileName(file.FileName);

The problem is when you actually try to save a file and send back a success response from the server. I don't think your demos are doing any of that. The iframe in ie9 does not receive the response from the server. The browser thinks the response is a download even though it's just a plain text json response. I debugged it down to the fact that the on load event on the iframe never gets fired so the onload handler that needs to handle this response is not doing anything. In all other browsers this is working.
Source: http://www.kendoui.com/forums/kendo-ui-web/upload/async-uploader-and-ie-9-not-working.aspx

Related

Save file to path desktop for current user

I have a project ASP.NET Core 2.0 MVC running on IIS.
Want to Export some information from data grid to Excel and save it from web page to the desktop of current user.
string fileName = "SN-export-" + DateTime.Now + ".xlsx";
Regex rgx = new Regex("[^a-zA-Z0-9 -]");
fileName = rgx.Replace(fileName, ".");
string path = Environment.GetFolderPath(Environment.SpecialFolder.Desktop);
string fileName2 = Path.Combine(path, fileName);
FileInfo excelFile = new FileInfo(fileName2);
excel.SaveAs(excelFile);
This works perfect local at Visual Studio, but not after publishing at IIS.
Using simple path string path = #"C:\WINDOWS\TEMP"; It will save this export file at the server temp folder, but not current web page user.
How to get this?
ASP.NET MVC is framework for a web application. So you have fronted and backend parts. This code will executed on the server side of your application. Even if you use Razor pages, they also generated at the backend. So there are several ways to save data on the computer:
use js to iterate data and save it, but I'm not sure that saving to excel with js is easy;
send desired data to backend, save it to excel and then return to the client.
For a second way you can use next code:
[Route("api/[controller]")]
public class DownloadController : Controller {
//GET api/download/12345abc
[HttpGet("{id}"]
public async Task<IActionResult> Download(YourData data) {
Stream stream = await {{__get_stream_based_on_your_data__}}
if(stream == null)
return NotFound();
return File(stream, "application/octet-stream"); // returns a FileStreamResult
}
}
And because of security reasons you can save data only to downloads directory.

Response Header issue on Azure Web Application

I am not sure what is happening here.
When I run my web application locally and click a button to download a file, the file is downloaded fine and Response header as you can see in the attached screenshot where it says local.
But when I publish the application to azure web app. Somehow the download button stops working. I checked the Response Header and you can see the difference.
What would cause this problem? The code is the same? Is there any settings that I should be setting in azure web app in azure portal?
Updated to add code
I have debugged remotely to figure out what is going on as #Amor suggested.
It is so strange that When I debug on my local machine first ExportTo action gets hit which prepares the TempData then Download action gets called once the first action completed with ajax call.
However, this is not the case when I debug remotely. Somehow the ExportTo action never gets called. It directly calls the Download action. As a result the TempData null checking is always null.
But why? Why on earth and how that is possible? Is there something cached somewhere?
I have wiped the content of web application on the remote and re-publish evertyhing to ensure everything is updated. But still no success.
here is the code:
[HttpPost]
public virtual ActionResult ExportTo(SearchVm searchVm)
{
var data = _companyService.GetCompanieBySearchTerm(searchVm).Take(150).ToList();
string handle = Guid.NewGuid().ToString();
TempData[handle] = data;
var fileName = $"C-{handle}.xlsx";
var locationUrl = Url.Action("Download", new { fileGuid = handle, fileName });
var downloadUrl = Url.Action("Download");
return Json(new { success = true, locationUrl, guid = handle, downloadUrl }, JsonRequestBehavior.AllowGet);
}
[HttpGet]
public ActionResult Download(string fileGuid, string fileName)
{
if (TempData[fileGuid] != null)
{
var fileNameSafe = $"C-{fileGuid}.xlsx";
var data = TempData[fileGuid] as List<Company>;
using (MemoryStream ms = new MemoryStream())
{
GridViewExtension.WriteXlsx(GetGridSettings(fileNameSafe), data, ms);
MVCxSpreadsheet mySpreadsheet = new MVCxSpreadsheet();
ms.Position = 0;
mySpreadsheet.Open("myDoc", DocumentFormat.Xlsx, () =>
{
return ms;
});
mySpreadsheet.Document.Worksheets.Insert(0);
var image = Server.MapPath("~/images/logo.png");
var worksheet = mySpreadsheet.Document.Worksheets[0];
worksheet.Name = "Logo";
worksheet.Pictures.AddPicture(image, worksheet.Cells[0, 0]);
byte[] result = mySpreadsheet.SaveCopy(DocumentFormat.Xlsx);
DocumentManager.CloseDocument("myDoc");
Response.Clear();
//Response.AppendHeader("Set-Cookie", "fileDownload=true; path=/");
Response.ContentType = "application/force-download";
Response.AddHeader("content-disposition", $"attachment; filename={fileNameSafe}");
Response.BinaryWrite(result);
Response.End();
}
}
return new EmptyResult();
}
here is the javascript:
var exportData = function (urlExport) {
console.log('Export to link in searchController: ' + urlExport);
ExportButton.SetEnabled(false);
var objData = new Object();
var filterData = companyFilterData(objData);
console.log(filterData);
$.post(urlExport, filterData)
.done(function (data) {
console.log(data.locationUrl);
window.location.href = data.locationUrl;
});
};
When Export button is clicked exportData function is called:
var exportToLink = '#Url.Action("ExportTo")';
console.log('Export to link in index: '+exportToLink);
SearchController.exportData(exportToLink);
As I mentioned that this code works perfectly on the local machine. something weird is happening on azure webapp that ExportTo action breakpoint is never gets hit.
I am not sure what else I could change to get the ExportTo action hit?
Based on the Response Header of Azure Web App, we find that the value of Content-Length is 0. It means that no data has been sent from web app server side.
In ASP.NET MVC, we can response file using following ways.
The first way, send the file which hosted on server. For this way, please check whether the excel file has been uploaded to Azure Web App. You could use Kudu or FTP to the folder to check whether the file is exist.
string fileLocation = Server.MapPath("~/Content/myfile.xlsx");
string contentType = System.Net.Mime.MediaTypeNames.Application.Octet;
string fileName = "file.xlsx";
return File(fileLocation, contentType, fileName);
The second way, we can read the file from any location(database, server or azure storage) and send the file content to client side. For this way, please check whether the file has been read successfully. You can remote debug your azure web app to check whether the file content hasn't been read in the right way.
byte[] fileContent = GetFileContent();
string contentType = System.Net.Mime.MediaTypeNames.Application.Octet;
string fileName = "file.xlsx";
return File(fileContent, contentType, fileName);
5/27/2017 Update
Somehow the ExportTo action never gets called. It directly calls the Download action. As a result the TempData null checking is always null.
How many instances does your Web App assigned? If your Web App have multi instances, the ExportTo request is handled by one instance and the Download request is handled by another instance. Since the TempData is store in memory of dedicated instance, it can't be got from another instance. According to the remote debug document. I find out the reason why the ExportTo action never gets called.
If you do have multiple web server instances, when you attach to the debugger you'll get a random instance, and you have no way to ensure that subsequent browser requests will go to that instance.
To solve this issue, I suggest you response the data directly from the ExportTo action or save the temp data in Azure blob storage which can't be accessed from multi instances.

MVC - Excel download on button click Cannot redirect after HTTP headers have been sent

Firstly, I know there are multiple posts related to this error but nothing is helping so far.
I am getting an error "Cannot redirect after HTTP headers have been sent." in my MVC razor application when I make a call to SomeController.
How do I fix this?
[HttpPost]
public ActionResult SomeController(Object abc)
{
Helper.somemethod("","excel");
return View(abc);//I tried this
return RedirectToAction("SomeController"); //I tried this also
}
public static void somemethod(string settocken, string filenames, List<Sample> samples)
{
//Extra logic for excel that uses List<Sample> to generate excel.
HttpContext.Current.Response.Clear();
HttpContext.Current.Response.Cookies.Add(new HttpCookie("downloadToken", settocken));
HttpContext.Current.Response.ContentType = "Application/x-msexcel";
HttpContext.Current.Response.AddHeader("Content-Disposition", String.Format("attachment; filename={0}.xlsx", filenames));
using (MemoryStream ms = new MemoryStream())
{
try
{
book.Save(ms);
HttpContext.Current.Response.BinaryWrite(ms.ToArray());
}
catch
{
}
finally
{
}
HttpContext.Current.Response.End();
}
}
#CoolArchTek http communication loop will be ended after HttpContext.Current.Response.End() call, so you can't add anything after that, however as far as I know there is no possibility in http to do what you want, so I would recommend you render your view with javascript which will initiate file download by client's browser
I would suggest, have an iframe inside view where you want to redirect to and show the data. And set the excel file path(or the action which returns excel file) as source for that iframe.
With this, as soon as the page loads, download begins.
Hope it helps.

How to open IE with post info in C#?

I am developing a small program which as a windows form. On this form, I want to put a link, and when user click the link, a seperate IE browser will be open with post data.
Original, i used System.Diagnostics.Process.start(). However, I can not post data through this kind of call.
And I searched the web, some guy suggested to use "Microsoft web browser control", however, this need to add this control in my form,and display the explorer in the form, but I want a seperate IE opened.
And I saw in VB there is a method as CreateObject("InternetExplorer.Application"), but I can not find how to use it in C#.
So, do you have any suggestions on how to implement?
Drop a web browser on your form. It should have a default name of "webBrowser1" - you can change that if you like. Set the "Visible" property to "False". Double-click the form title bar to auto generate a load event in the code.
Call the Navigate method, which has this signature:
void Navigate(string urlString, string targetFrameName, byte[] postData, string additionalHeaders);
Like this:
private void Form1_Load(object sender, EventArgs e)
{
webBrowser1.Navigate("http://www.google.com/", "_blank", Encoding.Default.GetBytes("THIS IS SOME POST DATA"), "");
}
You can pass any array of bytes you want in there... Encoding.Default.GetBytes() is just a fast way to pass a string through.
The trick is to use "_blank" for the target frame.
If you do a ShellExecute with a verb of OPEN on the url then the default web browser will be spawned and open the link. Alternatively, you can invoke Internet Explorer (once again using ShellExecute) with the url appended at the end of the string (so the string that you use for ShellExecute would look like this:
System.Diagnostics.Process.Start(#"C:\Program Files\Internet Explorer\iexplore.exe", "http://google.com");
You are talking about POST though, you cannot do a POST, the above line does a GET. Depending on how the website is set up you may be able to just append the parameters on the end of the url, like so:
System.Diagnostics.Process.Start(#"C:\Program Files\Internet Explorer\iexplore.exe", "http://www.google.com/search?q=bing");
You are definitely going to need to use Process.Start (or use a ProcessInfo) to get IE started : like this :
// open IE to a file on the Desktop as a result of clicking a LinkLabel on a WinForm
internal static string desktopPath = Environment.GetFolderPath(Environment.SpecialFolder.DesktopDirectory);
private void linkLabel1_LinkClicked(object sender, LinkLabelLinkClickedEventArgs e)
{
System.Diagnostics.Process.Start("IExplore.exe", desktopPath + "\\someHTMLFile.htm");
}
If you scroll down in this page :
http://msdn.microsoft.com/en-us/library/system.diagnostics.process.start(VS.80).aspx
(the Process.Start docs for FrameWork 3.0)
you'll find a user contributed example of using ProcessInfo to control whether more than one of instance of IE is started, and how to pass command line arguments to IE.
This page :
http://msdn.microsoft.com/en-us/library/0w4h05yb.aspx
(the Process.Start docs for FrameWork 3.5)
shows you a complete example of launching IE, and how to pass url files as arguments.
I'm not completely clear on what you mean by "Post" in your message (I associate "Post" with ASP.NET), but you could write out an html file in a temporary location with whatever you liked in it, and then pass the address of that file when you launch IE using the techniques documented above. best,
You can also use .NET reflection to open a browser
This example shows you how to set some specific attributes of the InternetExplorer.Application
For example, I needed to be able to turn off the address bar and set the height and width. IE and other browser security does not allow you to turn off the address bar in the other examples
Our site is an internal MVC application and works with no issues.
System.Type oType = System.Type.GetTypeFromProgID("InternetExplorer.Application");
object IE = System.Activator.CreateInstance(oType);
IE.GetType().InvokeMember("menubar", System.Reflection.BindingFlags.SetProperty, null, IE, new object[] { 0 });
IE.GetType().InvokeMember("toolbar", System.Reflection.BindingFlags.SetProperty, null, IE, new object[] { 0 });
IE.GetType().InvokeMember("statusBar", System.Reflection.BindingFlags.SetProperty, null, IE, new object[] { 0 });
IE.GetType().InvokeMember("addressbar", System.Reflection.BindingFlags.SetProperty, null, IE, new object[] { 0 });
IE.GetType().InvokeMember("Visible", System.Reflection.BindingFlags.SetProperty, null, IE, new object[] { true });
IE.GetType().InvokeMember("Height", System.Reflection.BindingFlags.SetProperty, null, IE, new object[] { 680 });
IE.GetType().InvokeMember("Width", System.Reflection.BindingFlags.SetProperty, null, IE, new object[] { 1030 });
IE.GetType().InvokeMember("Navigate", System.Reflection.BindingFlags.InvokeMethod, null, IE, new object[] { "http://yoursite" });
The only drawback here is that this is opening IE specifically. The plus is that it gives you more control over the browser.
You also have access to the Events, Methods and Properties of the InternetExplorer.Application object.
https://msdn.microsoft.com/en-us/library/aa752084(v=vs.85).aspx
I hope that helps someone else as it did me.
I am working on binding to events and will update this after testing.
You can just start process by sending URL in Process.Start as parameter. There is a problem while calling StartProcess from WebForms GUI thread because of synchronization context. My solution uses thread pool for this purpose. Advantage of this solution is that an URL is opened in user preferred web browser that can be IE, Firefox etc.
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming",
"CA1725:ParameterNamesShouldMatchBaseDeclaration", MessageId = "0#"),
System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes")]
public void OpenUrl(string urlString)
{
try
{
ThreadPool.QueueUserWorkItem(delegate { StartProcess(urlString, null); });
}
catch (Exception ex)
{
log.Error("Exception during opening Url (thread staring): ", ex);
//do nothing
}
}
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes")]
public void StartProcess(string processName, string arguments)
{
try
{
Process process = new Process();
process.StartInfo.FileName = processName;
if (!string.IsNullOrEmpty(arguments))
{
process.StartInfo.Arguments = arguments;
}
process.StartInfo.CreateNoWindow = false;
process.StartInfo.UseShellExecute = true;
process.Start();
}
catch (Exception ex)
{
log.ErrorFormat("Exception in StartProcess: process: [{0}], argument:[{1}], exception:{2}"
, processName, arguments, ex);
}
}
Actually, you can use process.start with posted query string data:
System.Diagnostics.Process.Start("IExplore.exe", "http://localhost/file.html?foo=bar&baz=duh");

Downloading a file onto client in ASP.NET MVC application using JQuery

I have a ASP.NET MVC controller with a method which needs to serve up a file to the client(browser). The controller action is invoked with a JQuery $.ajax call. The user needs to be prompted to download the file once the controller action has finished.
I used Response.Transmitfile/Response.WriteFile in the controller method but both have not prompted me to download the file in IE browser even though the file has been created and I am using the right file path as well.
When i invoke the same method directly by typing the controller URL in the browser I am prompted immediately to download the file created.
Could anyone let me know if there is something missing in this flow ?
I suspect it is the way I am calling the controller action in JQuery. How do i use the response of the JQuery Ajax call to ensure the client is prompted to download the file ?
[AcceptVerbs(HttpVerbs.Post)]
public ActionResult CreatePresentation(string id)
{
//do something here , create the file and place in a location on the server itself
string filename = Path.GetFileName(filePath);
Response.ContentType = "APPLICATION/OCTET-STREAM";
System.String disHeader = "Attachment; Filename=\"" + filename +
"\"";
Response.AppendHeader("Content-Disposition", disHeader);
FileInfo fileToDownload = new FileInfo(filePath);
Response.WriteFile(fileToDownload.FullName);
}
On the Javascript side, this is how i invoke the controller action
function CreatePresentation()
{
// something here
$.ajax({
type: "POST",
url: "http://localhost:4844/ActionBar/CreatePresentation",
data:data
});
} // end of function
when you use $.ajax, or for that matter any other AJAX mechanism, you're going around the normal browser file-transfer pipeline. It's the main pipeline that trigger's the browser's Save This File dialog, not the AJAX one.
To achieve what you want, you'll want to use a synchronous location change rather than an asynchronous one: rather than using $.ajax, just set document.location:
function CreatePresentation()
{
//snip code that creates a map called "data"
var paramSnippets = [];
for (item in data)
{
paramSnippets.push(item + "="+data[item]);
}
document.location = "http://localhost:4844/ActionBar/CreatePresentation" + "?" + paramSnippets.join("&");
}
edited in response to comments: included example

Resources