I have a delegate method with is called periodic while WritingAnObject uploading the file. I would like to update div (ProgressUpdate) in my MVC page with args.PercentDone value. I appreciate any idea?
Thanks,
//delegate method
private void displayProgress(object sender, ProgressArgs args)
{
//Console.WriteLine(args.PercentDone); //I want to display args.PercentDone in the page
}
//Controller
[HttpPost]
public ActionResult WritingAnObject(MyViewModel bovModel)
{
//DoSomeStuff which is cause calling displayProgress
return RedirectToAction("ListingSomeInfo", "testpart");
}
//View
<%using (Html.BeginForm("WritingAnObject", "testpart", FormMethod.Post, new { enctype = "multipart/form-data" }))
{%>
<%:Html.TextBox("catname") %>
<input type="file" id="fileupload" name="fileupload" />
<input type="submit" value="Upload" />
<%} %>
<div id= “ProgressUpdate”</div>
Here is one approach you could take to display progress back to a user while an operaton on the server is completing. (requires javascript)
1) Write an action that starts the process on the server. This method should update a progress value in session state (so that it is specific to each session the user is running).
2) Write an action that the client can call to return progress. This would read the value in session state. Generally this action will return either a small HTML fragment containing the progress bar filled in to the right amount, or a JSON object containing the progress value.
3) From your client, make a jQuery.ajax() call to asynchronously poll the server for progress while the operation is running and update the UI.
Additional bells&whistles:
- an Action to cancel a long running operation
- running the task outside the web application (Azure has some excellent features regarding running tasks asynchronously from a web app)
- Have the action that returns progress also let the client know if the operation is completed or canceled.
Related
I am uploading a file using via a view linked to a controller however after the upload is uploaded the application is either trying to refresh or redirect and I need to prevent this. May you please point me in the right direction to avoid redirection and refresh? I have done a bit of reading and I suspect that this line action="/api/BulkUpload">might be causing the problem.
My Controller
using System;
using System.Diagnostics;
using System.IO;
using System.Net;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Text;
using System.Threading.Tasks;
using System.Web;
using System.Web.Http;
using Repositories.BulkUpload;
using Repositories.Interfaces.BulkUpload;
namespace SimSentinel.Controllers
{
//[Authorize]
public class BulkUploadController : ApiController
{
private readonly IBulkUploadRepository _bulkUploadRepository;
public async Task<HttpResponseMessage> PostFile()
{
// Check if the request contains multipart/form-data.
if (!Request.Content.IsMimeMultipartContent())
{
throw new HttpResponseException(HttpStatusCode.UnsupportedMediaType);
}
string root = HttpContext.Current.Server.MapPath("~/Files");
var provider = new FormDataStreamer(root);
try
{
StringBuilder sb = new StringBuilder(); // Holds the response body
// Read the form data and return an async task.
await Request.Content.ReadAsMultipartAsync(provider);
// This illustrates how to get the form data.
foreach (var key in provider.FormData.AllKeys)
{
foreach (var val in provider.FormData.GetValues(key))
{
sb.Append(string.Format("{0}: {1}\n", key, val));
}
}
// This illustrates how to get the file names for uploaded files.
foreach (var file in provider.FileData)
{
FileInfo fileInfo = new FileInfo(file.LocalFileName);
sb.Append(string.Format("Uploaded file: {0} ({1} bytes)\n", fileInfo.Name, fileInfo.Length));
}
return new HttpResponseMessage()
{
Content = new StringContent(sb.ToString())
};
}
catch (System.Exception e)
{
return Request.CreateErrorResponse(HttpStatusCode.InternalServerError, e);
}
}
public class FormDataStreamer : MultipartFormDataStreamProvider
{
public FormDataStreamer(string rootPath) : base(rootPath) { }
public FormDataStreamer(string rootPath, int bufferSize) : base(rootPath, bufferSize) { }
public override string GetLocalFileName(HttpContentHeaders headers)
{
var srcFileName = headers.ContentDisposition.FileName.Replace("\"", "");
return Guid.NewGuid() + Path.GetExtension(srcFileName);
}
}
}
}
MY HTML
<form name="form1" method="post" enctype="multipart/form-data" action="/api/BulkUpload">
<div>
<label for="caption">Image Caption</label>
<input name="caption" type="text" />
</div>
<div>
<label for="image1">Image File</label>
<input name="image1" type="file" />
</div>
<div>
<input type="submit" value="ok" />
</div>
</form>
You're correct. When you submit the form, the file is sent to the controller via a HTTP POST request and the page is either, necessarily, refreshed or redirected. If you don't want to the page to refresh or redirect, then you'll have to use AJAX to post the file to the controller.
From a Mozilla Developer document on HTTP requests,
The GET method requests a representation of the specified resource.
Requests using GET should only retrieve data.
The POST method is used to submit an entity to the specified
resource, often causing a change in state or side effects on the
server.
From these notes on Web Programming from Nanyang Technological University,
[The] POST request method is used to "post" additional data up to the server
(e.g., submitting HTML form data or uploading a file). Issuing an HTTP
URL from the browser always triggers a GET request. To trigger a POST
request, you can use an HTML form with attribute method="post" or
write your own network program. For submitting HTML form data, POST
request is the same as the GET request except that the URL-encoded
query string is sent in the request body, rather than appended behind
the request-URI.
So, you can see that since you're posting a file to the server using a standard HTTP request, it is necessarily going to refresh or redirect in some way.
To avoid this, you can use jQuery to asynchronously post the file to the server without refreshing the page. There are plenty of articles on how to do this. I suggest you give it a try and post another question if you get stuck.
Upload file using jQuery and post it to Controller
ASP Snippets - Upload file using jQuery AJAX in ASP.Net MVC
C# Corner - File Upload Through JQuery AJAX In ASP.NET MVC
Thanks so much for the help it guided me in the right direction. I eventually got my answer from this [How to submit html form without redirection? . The Iframe approach is the simplest approach it is a temporary fix seeing as some articles are saying that although it is still supported by most modern browsers it has been deprecated.
I'm trying to assign value to the hidden field in java script using the
JavaScript variable and trying to pass it back to the controller. The value every time I go in the post method for my model property DBErrorID is 0.
Razor View:
<body>
#Html.HiddenFor(model => model.DBErrorID, new { id = "DBErrorId" })
<input type="submit" value="Update" class="btn btn-success" onclick="GetValue()" />
</body>
JavaScript:
<script language="javascript" type="text/javascript">
function GetValue() {
$("#DBErrorId").val(totalValues);
// alert(totalValues);
}
<script>
Controller:
[HttpPost]
public ActionResult ErrorStatusView(Models.ErrorStatusModel obj)
{
Models.ErrorStatusModel objreg = new Models.ErrorStatusModel();
objreg.DBErrorID = obj.DBErrorID;
}
Your current server side code is creating an unnecessary new object of ErrorStatusModel as the first line, which will create a new object(objreg variable) with default values(unless you are setting it in a constructor), for an int type property it will be 0. If you are inspecting the values of objreg, that is the reason you see 0 as the property value.
You do not need to create this additional object. The model binder framework will create one for you and map the posted values, when you use ErrorStatusModel your method parameter type. That means your obj property is properly populated by the form data (assuming the DBErrorID property is settable)
[HttpPost]
public ActionResult ErrorStatusView(Models.ErrorStatusModel obj)
{
// obj is populated with form values.
// use obj
// return something.
}
Also, your client side code is trying to set the value of hidden input inside the GetValue method which is called on the onclick event of the submit button. If you are using a normal form submit and your button is inside a form tag, when user clicks on the submit button it will immediately submit the form (with the current value of that input)
If that is the case, you should prevent the default behavior (submitting the form) when the button is clicked, set the value as needded and fire the form submit via JavaScript.
There are multiple ways to do it. Here is one approach- the unobtrusive JavaScript approach- which assumes you have jQuery loaded to your page. Give an Id to the button, which we can use to wireup the click event.
<input type="button" value="Update" class="btn btn-success"
id="btn-save" />
Now in JavaScript, listen to the click event on this button, prevent the normal behavior(submitting the form), update the hidden input value and trigger the form submit.
$(function () {
$('#btn-save').click(function (e) {
e.preventDefault();
$("#DBErrorId").val(34);
$(this).closest("form").submit();
});
})
Also you should not create a new object in server
I have long operation which retrieves data from my Active Directory and than shows this data in MVC view as table. All works except that it takes 20 seconds to present data. I have question how can i avoid UI blocking and show spinner while data is loading.
You can show a loading grpahic, then make an ajax call to fetch the data and when you receive a response from your asynchronous ajax call, show that to the user and hide(or replace) the loading image.
<div id="userTable">
<p>Loading...</p>
<img src="~/Content/images/loadingimage.png" alt="Please wait" />
</div>
and in the document ready event make the ajax call to get the data you want. You can use the jquery load() method.
$(function(){
$("#userTable").load("#Url.Action("UserList","Users")");
});
Assuming UserList() action method in UsersController will return a partial view with the markup of tabular data for your user list.
public ActionResult UserList()
{
var useViewModelList = new List<YourUserViewModel>();
useViewModelList.Add(new YourUserViewModel { Name="Scott" });
return PartialView(useViewModelList);
}
And in your partial view ( ~/Views/Shared/UserList.cshtml),
#model List<YourUserViewModel>
<table>
<tr><th>Name</th></tr>
#foreach(var item in Model)
{
<tr><td>#item.Name</td></tr>
}
</table>
suppose i have one form and form has two div container. one div container has few textboxes for submiting login details and another div also has few textboxes those related to register. now my home controller has two action method one is login and another is register. i want that when user click on login button then i will submit form through jquery but before submit i will change action url. the same way i want to call register action method when user click on register button. please guide me how could i do this in mvc with jquery. please help me with sample code.
another person answer like in other forum.
#model MvcForums.Models.StudentModel
#{
using(#Html.BeginForm("Create","Student", FormMethod.Post))
{
<label> Name</label>
#Html.TextBoxFor(m=>m.FirstName) <br/>
<label> From country:</label>
#Html.DropDownListFor(m=>m.CountryId, Model.Countries,"--please select-- ") <br/>
<input id="btnSave" value ="Login" type="submit" name="commandName"/>
<input id="btnRegister" value ="Register" type="submit" name="commandName"/>
}
}
[HttpPost]
public ActionResult Create(StudentModel studentModel, string commandName)
{
if(commandName == "Login")
{
}
else if(commandName == "Register")
{
}
return View("Index");
}
after reading his answer some confusion occur in my mind because i am newbie in mvc and those are as follows
first of all ur code is not readable for ill format. i am new in mvc....just learning it. after reading ur code some confusion occur.
1) why extra bracket u gave #{ }
2 u use <label> Name</label> in form building code. in html4 is there any tag ? is it html5 specific tag ?
3) #Html.DropDownListFor(m=>m.CountryId, Model.Countries,"--please select-- ")
when u building dropdown why u did not specify value & text filed.......how mvc can understand what field will be value & what will be text ? u just bind the model with DropDownListFor()
4) just see it
<input id="btnSave" value ="Login" type="submit" name="commandName"/>
<input id="btnRegister" value ="Register" type="submit" name="commandName"/>
both the button name is commandName ? is it correct ?
when user click on any button then how button name will be pass to
public ActionResult Create(StudentModel studentModel, string commandName)
Create() method ? please tell me how implicitly command name will be pass to Create() and how commandName variable at server side can hold the button name.
i have too much confusion after reading ur code. if possible please discuss all my points in details. thanks
i want that when user click on login button then i will submit form
through jquery but before submit i will change action url. the same
way i want to call register action method when user click on register
button.
1. Event on button click:
$(function(){
$("#btnSave").click(function(){
$("#btnSave").closest("form").attr("action", "/home/test");
});
});
2. MarkUp:
<form method="POST" action="/home/test1" >
<input id="btnSave" value="Login" type="submit" name="commandName"/>
</form>
3. Server side:
[HttpPost]
public ActionResult Test(string commandName)
{
return null;
}
1) Razor syntax reference http://haacked.com/archive/2011/01/06/razor-syntax-quick-reference.aspx
2) The <label> is not specific html5 tag but it have additional attribute in html5, http://www.w3schools.com/tags/tag_label.asp
3) You can build your IEnumerable of SelectListItem in which specified Text and Value
4) The input name commandName is not important if you don't want to catch client value value ="Login" on server side,
I have
public class FileController : AsyncController
{
public ActionResult Download(FormCollection form)
{
FileContentResult file = new FileContentResult(Encoding.UTF8.GetBytes("10k file size"),"application/vnd.xls");
file.FileDownloadName = "test.xls";
return file;
}
}
and ofcourse, ajax form
<% var form = Ajax.BeginForm(...) %>
<input type="image" src="...gif" /> // this is my 1st attempt
<%= Ajax.ActionLink(...) %> // 2nd attempt
<% form.EndForm(); %>
i try first method(input type=image). it reach correct Action. but no file download in client side.
Then i try to use Ajax.ActionLink which i really hate. i want nice image button, not link text. Again, it reach correct Action and no file download. But if i open link in another window, there's file download !!
Q. How to make a nice file downlaod with AsyncController
Q. How to make Ajax.ActionLink lok nice
You cannot use Ajax to download files from the server. The reason for this is that even if you succeed to make the async request to the server in the success callback you will get the file contents as sent from the server and you cannot do much with this file on the client side. Remember that javascript cannot access the file system so you won't be able to save it. The way to achieve this is to have a normal HTML <form> which will point to the Download action. When this form is submitted the user will be asked to choose where he wants to save the file and the download will proceed. Also you don't need an AsyncController for this.
Here's an example:
public class FileController : Controller
{
[HttpPost]
public ActionResult Download()
{
return File(
Encoding.UTF8.GetBytes("10k file size"),
"application/vnd.xls",
"test.xls"
);
}
}
and inside your view:
<% using (Html.BeginForm("download", "file", FormMethod.Post)) { %>
<input
type="image"
src="<%: Url.Content("~/content/images/download.png") %>"
value="download"
alt="download"
/>
<% } %>
And in order to make the download button look nice, you could create a nice download.png image which will be used as form submit button.
I use this:
to disable
$('form').removeAttr("data-ajax");
to enable
$('form').attr("data-ajax", "true");
for a form with 3 submit 1 to refresh 2 to download file
All post to the same ActionResult but 2 of them have param to download as csv or as pdf
when download just disable ajax submission.
i found a way!!
just create iframe that has regular form and use jquery to trigger to.
$("iframe").contents().find("form").submit();
:D
ps credit to Firebug + Google Doc.