I'm currently working on a project where the previous contractor had an attachments area within our site. The piece works for the most part but has issues when redirecting back after uploading the file, plus I don't like the fact the page does a full page reload just to update a grid to show the uploaded file.
My goal is to instead do an Ajax call for the upload versus form submit. I have added this in, however, the return forces a download of the Json object (using IE 11). I have researched how to get around this and have yet to find any substantial ways around it.
Is it possible to upload a file using Ajax and not send back a download of the Json object?
Below is my code.
View (Upload.cshtml)
#using (Html.BeginForm("Upload", "PM", FormMethod.Post, new { enctype = "multipart/form-data", id = "frmUpload" }))
{
#Html.ValidationSummary(true)
<table>
...
<tr>
<td>#Html.Label("File: ")</td>
<td>
<input type="file" name="file" id="file"/>
#Html.ValidationMessage("file","File is required")
</td>
</tr>
...
<tr>
<td colspan="2">
<p>
<button type="submit" class="t-button" id="btnSubmit">
Attach</button>
<button type="button" class="t-button" onclick="CloseAttachmentWindow()">
Cancel</button>
</p>
</td>
</tr>
</table>
}
<script type="text/javascript">
$(document).ready(function () {
$("#btnSubmit").click(function (e) {
e.preventDefault();
if (!$('form').valid())
return false;
//Upload document
$.ajax({
type: "POST",
cache: false,
url: "/PM/Upload",
dataType: "json",
contentType: false,
processData: false,
data: $('form').serialize(),
success: function (result) {
if (result.success) {
var window = $("#error").data("tWindow");
window.content("<b>Attachment successfully added</b>").title("Success!");
window.center().open();
CloseAttachmentWindow();
}
else {
var window = $("#error").data("tWindow");
window.content("<b>Error: Unable to Upload Document. Please try again. "
+ "If this fails, contact the administrators with the below details.</b>"
+ '\n' + '\n' + result.Error).title("Error");
window.center().open();
}
},
error: function (xhtr, e, e2) {
var window = $("#error").data("tWindow");
window.content(e + '\n' + xhtr.responseText, 'error', '');
window.center().open();
}
});
});
});
</script>
PMController.cs
[HttpPost]
public ActionResult Upload(HttpPostedFileBase file, FormCollection formcollection)
{
if (file != null)
{
var cntPOC = int.Parse(Session["cntPOC"].ToString());
try
{
var cntFileType = _fileTypeRepo.GetCntFileTypeByMimeType(file.ContentType);
if (cntFileType == 0)
throw new Exception("This file type is not supported");
var strAttachmentName = formcollection["AttachmentName"];
var strAttachmentType = formcollection["AttachmentType"];
var length = file.ContentLength;
var tmpFile = new byte[length];
if (tmpFile.Count() > 0)
{
file.InputStream.Read(tmpFile, 0, length);
var intAttchmentId = _AttachRepo.GetNextAttachmentId() + 1;
var objAttachment = new TBLATTACHMENT
{
CNTATTACHMENT = intAttchmentId,
CNTPOC = cntPOC,
CNTFILETYPE = cntFileType,
CNTATTACHMENTTYPE = Convert.ToDecimal(strAttachmentType),
DTMCREATED = DateTime.Now,
STRATTACHMENTTITLE = strAttachmentName,
BLBATTACHMENT = tmpFile,
STRORIGINALFILENAME = file.FileName,
YSNDELETED = 0
};
_AttachRepo.Add(objAttachment);
_AttachRepo.Save();
return Json(new { success = true, Error = "" });
}
//File not real
else
return Json(new { success = false, Error = "Please select appropriate file" });
}
catch (Exception ex)
{
logger.LogError("File Upload", ex);
if (ex.InnerException != null)
ModelState.AddModelError("Error", ex.InnerException.ToString());
else
ModelState.AddModelError("Error", ex.Message.ToString());
TempData["ModelState"] = ModelState;
return Json(new { success = false, Error = ex.Message });
}
}
else
{
logger.LogError("File Upload Error. File was not selected");
ModelState.AddModelError("Error", "Please select file");
TempData["ModelState"] = ModelState;
return Json(new { success = false, Error = "File was not selected" });
}
}
As is, using this code, I can upload documents, however, I get the prompt to download the Json object upon return.
NOTE Long story short, you cannot do this. I had to learn the hard way and never did find a solution. I did find a way to do it for downloads, but not uploads.
Options:
Remove change the button type submit to button <input type="button"/>
<input type="submit" onclick="return false">
return false; or add event handlers
$("input[type='submit']").click(function() { return false; });
or
$("form").submit(function() { return false; });
<form onsubmit="return false"> ...</form>
in order to avoid refresh at all "buttons", even with onclick assigned.
changing the submit type to button is the optimal one.
Related
I am using cshtml as view
this is my input:
#Html.TextArea("DESCRIPTION", null, new { #class = "field-longtext", #cols = 100, #rows = 5, maxlength = 255 })
#Html.TextBox("DOC_TYPE", null, new { #class = "field-longtext", maxlength = 10 })
#Html.TextBox("DOC_FILE", null, new { #class = "field-longtext", #type = "file" })
and using ajax post
function UploadFile() {
var url = '#Url.Action("CreateUploadFile")';
var data = {};
var result = false;
$('##ViewBag.FormName').find('input, textarea').each(function () {
data[$(this).attr('name')] = $(this).val();
});
data.PROBLEM_CALL_ID = #Model.Model.PROBLEM_CALL_ID;
$.ajax({
type: 'POST',
url: url,
data: data,
async: false,
success: function (data) {
if (data.result) {
var selectedRow = SubFormService.tableList['##ViewBag.TableName'].selectedRow;
result = true;
} else {
alert(data.errorMsg);
result = false;
}
}
});
return result;
}
My Controller:
[HttpPost]
public ActionResult CreateUploadFile(Models.Shared.DocumentModel vm)
{
var id = -1;
var result = string.Empty;
var json = new BasicJsonResult();
var file = vm.DOC_FILE;
LogUtility.Debug(file.ContentLength.ToString());
if (file != null && file.ContentLength > 0)
{
var fileName = Path.GetFileName(file.FileName);
LogUtility.Debug(fileName);
LogUtility.Debug(Server.MapPath("~/Images/"));
var path = Path.Combine(Server.MapPath("~/Images/"), fileName);
file.SaveAs(path);
}
return Json(json, JsonRequestBehavior.AllowGet);
}
the code var file = vm.DOC_FILE; warns me (Object reference not set to an instance of an object.), but I can get another control value (vm.DESCRIPTION, vm.DOC_TYPE).
Please help.
Also, I want to download file when that file is uploaded.
With the introduction of XHR2 it is now possible to upload files using AJAX. You could use FormData to achieve this. Assuming that your input elements are inside a <form> you may try this (also include in this form a hidden input for the PROBLEM_CALL_ID field so that it gets sent to the server):
function UploadFile() {
var url = '#Url.Action("CreateUploadFile")';
var formData = new FormData($('form')[0]);
var result = false;
$.ajax({
type: 'POST',
url: url,
data: formData,
async: false,
success: function (data) {
...
}
});
return result;
}
So just make sure that you have wrapped your input elements inside a form tag (preferably with an id so that you can more specifically select it):
<form id="myForm">
#Html.TextArea("DESCRIPTION", null, new { #class = "field-longtext", #cols = 100, #rows = 5, maxlength = 255 })
#Html.TextBox("DOC_TYPE", null, new { #class = "field-longtext", maxlength = 10 })
#Html.TextBox("DOC_FILE", null, new { #class = "field-longtext", #type = "file" })
#Html.HiddenFor(x => x.PROBLEM_CALL_ID)
</form>
and then select the form like this:
var formData = new FormData($('#myForm')[0]);
Also please, please, please don't do that:
async: false
By doing this you are defeating the whole purpose of an asynchronous call to the server. If you set this flag the browser will freeze during the file upload which is an absolutely horrible user experience. If you do this, then you'd better use a normal HTML form submit to upload the file - you are loosing all the benefits of AJAX and there's no point to be making an AJAX request with this flag set to true.
If you want to able upload a file using ajax, you should use iframe.
<iframe name="upload-data" id="upload-data" frameborder="0" width="0" height="0"></iframe>
<form id="frm-add-update" enctype="multipart/form-data" method="post" target="upload-data">
// HTML Code
</form>
In my case, my Controller would return a code, "0x001" if the upload process successful, otherwise it will return "0x00".
When document is ready
$("#upload-data").hide();
$("#upload-data").load(function () {
var data = $.parseJSON($("#upload-data").contents().text());
if (data != null) {
if (data.code != "0x001") {
// you got a problem
}
else {
// you got no problem
}
}
});
In Controller
[Authorize, HttpPost]
private JsonResult ActionNAme(ModelClass modelObj, HttpPostedFileBase htmlFileTagName)
{
string code = "0x001";
string message = "";
try
{
// your process here
}
catch(Exception)
{
code = "0x000";
message = ex.Message;
}
return new JsonResult()
{
Data = new
{
code = code,
message = message
}
};
}
For downloading the uploaded file, i think you should change the Action return type. You will get the information from this link.
Hope it can solve your problem :)
My Html code:
<div class="col-md-6">
<img ngf-src="!picFile.$error && picFile" style="height: 150px; width: 200px;">
<input type="file" ngf-select ng-model="picFile" name="file"
accept="image/*" ngf-max-size="2MB"><b>Picture</b><br />
</div>
<div class="col-md-6">
<img ngf-src="!sigFile.$error && sigFile" style="height: 150px; width: 200px;">
<input type="file" ngf-select ng-model="sigFile" name="file"
accept="image/*" ngf-max-size="2MB"><b>Signature</b><br />
</div>
And My angular code
$scope.SaveNewJoinHolder = function (picFile, sigFile) {
if (investor_validity == 1) {
if ($scope.newJoinHolderForm.$valid) {
if (typeof $scope.newJoinHolder.DOB == undefined) {
$scope.newJoinHolder.DOB = null;
}
else {
var datefilter = $filter('date');
$scope.newJoinHolder.DOB = datefilter($scope.newJoinHolder.DOB, 'dd/MM/yyyy');
$scope.newJoinHolder.birth_date = dateconfigservice.FullDateUKtoDateKey($scope.newJoinHolder.DOB);
}
Upload.upload(
{
url: '/InvestorManagement/JoinHolder/SaveNewJoinHolder',
method: 'POST',
fields: $scope.newJoinHolder,
file: { picFile: picFile, sigFile: sigFile },
async: true
})
.success(function () {
toastr.success('Submitted Successfully');
}).error(function () {
toastr.success('Failed');
});
}
}
};
I debugged the code and I got both of the file while debugging. But it is not calling my C# method
public JsonResult SaveNewJoinHolder(tblJoinHolder joinHolder, HttpPostedFileBase picFile, HttpPostedFileBase sigFile)
{
joinHolderFactory = new JoinHolderFactory();
try
{
joinHolder.membership_id = SessionManger.BrokerOfLoggedInUser(Session).membership_id;
joinHolder.changed_user_id = User.Identity.GetUserId();
joinHolder.changed_date = DateTime.Now;
joinHolder.is_dirty = 1;
byte[] image = new byte[picFile.ContentLength];
picFile.InputStream.Read(image, 0, picFile.ContentLength);
joinHolder.photo = image;
byte[] signature = new byte[sigFile.ContentLength];
sigFile.InputStream.Read(image, 0, sigFile.ContentLength);
joinHolder.signature = signature;
joinHolderFactory.Add(joinHolder);
joinHolderFactory.Save();
return Json(new { data = "Successfully Saved Data", success = true });
}
catch (Exception ex)
{
return Json(new { data = ex.Message, success = false });
}
}
What is the problem here?
If I try to upload single image it is working.
Before version 7.2.0 you couldn't specify a map as file option so you had to do
Upload.upload(
{
url: '/InvestorManagement/JoinHolder/SaveNewJoinHolder',
method: 'POST',
fields: $scope.newJoinHolder,
file: [picFile, sigFile],
fileFormDataName: ['picfile', 'sigFile'],
})
But since version 7.2.0 your original code should work.
You can verify the network tab of your browser to make sure that the file form data is being sent to the server.
I need the c# code to upload images to my web api. (c# code that will do the same as my ajax call below)
Here is my working example that I use to upload images from my web project via ajax call.
HTML
<form>
<span>Select file(s) to upload :</span>
<input id="file1" name="file1" type="file" multiple="multiple" />
<input id="button1" type="button" value="Upload" />
</form>
ajax Call
$(document).ready(function () {
$("#button1").click(function (evt) {
var files = $("#file1").get(0).files;
if (files.length > 0) {
var data = new FormData();
for (i = 0; i < files.length; i++) {
data.append("file" + i, files[i]);
}
$.ajax({
type: "POST",
url: window.apiuri + "/api/book/UploadCover()",
contentType: false,
processData: false,
data: data,
success: function (messages) {
for (i = 0; i < messages.length; i++) {
$.smkAlert({ text: messages[i], type: 'success' });
}
},
error: function () {
$.smkAlert({ text: "Error while invoking the Web API", type: 'success' });
}
});
}
});
});
API Code in c#
public async Task<List<string>> UploadCover()
{
try
{
List<string> messages = new List<string>();
if (Request.Content.IsMimeMultipartContent())
{
string path = string.Format(#"C:\");
API.Classes.ImageUploads.MyStreamProvider streamProvider = new API.Classes.ImageUploads.MyStreamProvider(path);
await Request.Content.ReadAsMultipartAsync(streamProvider);
foreach (var file in streamProvider.FileData)
{
FileInfo fi = new FileInfo(file.LocalFileName);
try
{
messages.Add("<b>Book Cover Uploaded</b>");
}
catch (Exception ex)
{
messages.Add("<b>Upload Failed</b>");
}
}
return messages;
}
else
{
HttpResponseMessage response = Request.CreateResponse(HttpStatusCode.BadRequest, "Invalid Request!");
throw new HttpResponseException(response);
}
}
catch (Exception ex)
{
HttpResponseMessage response = Request.CreateResponse(HttpStatusCode.BadRequest, "Request Failed!");
throw new HttpResponseException(response);
}
}
Please help me with c# code to upload images to my web api. Thank you
I an experimenting with MVC. I have a view which contains a dropdownlist and a table.
When I select an option from the dropdownlist, I want to get the data from my controller and update the view:
View:
<div>
<h2>Current Print Statistics</h2>
#using (Html.BeginForm())
{
#Html.DropDownList("LogTypes", new SelectList(Model.LogTypes, "Value", "Text"), new
{
id = "logType",
data_url = Url.Action("GetStatistics", "Home")
})
<table id="modelTable" class="table table-condensed">
<tr>
<th>RequestedOn</th>
<th>PrintedOn</th>
<th>Message</th>
<th>Success</th>
<th>TemplateName</th>
</tr>
<tbody>
#foreach (var item in Model.PrintLogs)
{
string css = (item.Success) ? "success" : "danger";
string link = (item.Success) ? "www.google.com" : string.Empty;
<tr class="#css">
<td>#item.RequestedOn</td>
<td>#item.PrintedOn</td>
<td>#item.Message</td>
#if (item.Success)
{
<td>#item.Success</td>
}
else
{
<td>#Html.ActionLink("False", "Index", "LogView", new { id = item.LogID }, null)</td>
}
<td>#item.TemplateName</td>
</tr>
}
</tbody>
</table>
}
</div>
</div>
<script src="~/Scripts/jquery-1.10.2.js"></script>
<script type="text/javascript">
$(function () {
$('#logType').change(function () {
console.log($(this).data('url'));
var selectedValue = $(this).val();
var table = $('#modelTable');
$.ajax({
url: $(this).data('url'),
type: 'GET',
cache: false,
context: table,
data: { value: selectedValue },
success: function (result) {
$.each(result.PrintLogs,
function (index, log) {
$('<tr/>', {
html: $('<td/>', {
html: log.RequestedOn
}).after($('<td/>', {
html: log.PrintedOn
})).after($('<td/>', {
html: log.Success
})).after($('<td/>', {
html: log.Message
})).after($('<td/>', {
html: log.TemplateName
}))
}).appendTo(tableBody);
}
);
}
});
});
});
</script>
Controller:
[HttpGet]
public JsonResult GetStatistics(string value)
{
var request = LogTypeRequest.Last24H;
if (value == "0") request = LogTypeRequest.Last24H;
if (value == "1") request = LogTypeRequest.LastWeek;
if (value == "2") request = LogTypeRequest.LastMonth;
var model = new PrintServerModel
{
LogTypes = new List<ListItem>
{
new ListItem() {Text = "Last 24 Hours", Value = "0"},
new ListItem() {Text = "Last Week", Value = "1"},
new ListItem() {Text = "Last Month", Value = "2"}
},
PrintLogs = PrintServerService.GetPrinterLog(request)
};
return Json(model, JsonRequestBehavior.AllowGet);
}
Now when I try to debug in chrome, when the line $.ajax({ is reached it seems to jump to the end.
Ideally what I want is to display the data on start up and then when the user selects something from the dropdown, refresh the data.
Any help greafully appreciated!!
Odds are there is an error from the json call, you should add .done or error: to the end of it so you can see what your error is.
also there is a tab in chrome debug tool for watching the network calls, you may be able to see the response from the call in there with some additional details.
just looking over the ajax call, may want to change to have
data: JSON.stringify( { value: selectedValue }),
if you get more info i will do what i can to better my answer.
I have a problem, Im trying to display a message on the same page after the form has been submitted, but my result is always false because my action in my controller gets called twice, when I click on the submit button, The first time around my action is called the data is passed through to the action with the correct info and is saved to the db, the second time around every parameter is null thus return false.
I want a way to submit only once and return the appropriate Json result in the same page without redirecting, I hope someone can help me with this, here is my code:
I tried:
function PdfHeaderAndFooterManager() {
$('#submitPdf').ajaxSubmit(function (e) {
$.ajax({
url: "/Dashboard/PdfHeaderAndFooterManager",
dataType: "json",
type:"POST",
data: {headerImage: headerImage, footerImage: footerImage},
success: function (data) {
if (data.success) {
alert(data.message);
$('#resultMessage').text(data.message).css("color", "RED");
}
else {
alert(data.message);
$('#resultMessage').text(data.message).css("color", "RED");
}
}
});
e.preventDefault();
});
}
My view some code removed for simplicity
#using (Html.BeginForm("PdfHeaderAndFooterManager", "Dashboard", FormMethod.Post, new { enctype = "multipart/form-data", id = "formPdfImages" }))
div id="resultMessage"></div>
}
public ActionResult PdfHeaderAndFooterManager(HttpPostedFileBase headerImage, HttpPostedFileBase footerImage)
{
//some code to declare variables
if (headerImage != null)
{
if (!String.IsNullOrEmpty(headerImage.ContentType))
{
headerImageContentType = imageHelper.IsValidImageType(headerImage.ContentType);
if (headerImageContentType)
{
resizedHeaderImage = imageHelper.ResizeImage(headerImage.InputStream);
}
else
{
return Json(new { success = false, message = "Please Upload an image* file less than 2GB." });
}
}
}
if (footerImage != null)
{
if (!String.IsNullOrEmpty(footerImage.ContentType))
{
footerImageContentType = imageHelper.IsValidImageType(footerImage.ContentType);
if (footerImageContentType)
{
resizedFooterImage = imageHelper.ResizeImage(footerImage.InputStream);
}
else
{
return Json(new { success = false, message = "Please Upload an image* file less than 2GB." });
}
}
}
if (P24DataPrincipal.CurrentIdentity != null)
{
if (resizedHeaderImage != null || resizedFooterImage != null)
{
//add to DB code
return Json(new { success = true, message = "Image(s) Uploaded Successfully." });
}
else
{
return Json(new {success = false, message = "Upload atleast 1 image file." });
}
}
return Json("someview");
}
and above is my action the most important parts are the "return" keywords I just want to see that in my view even when the result is true not only when the result is false and this action should be only called once and not redirect. Thanks.
Do you have an <input type="submit" />? If so, change it to a button and wire it up in your jQuery ready so that it hits your function:
$.ready(function(){
$("#submitButtonId").click(function() {
//put your ajax stuff here
});
});
Your first function actually looks close to being correct. A few small tweaks would look as per below.
Does the following work?
$(function() {
$('#formPdfImages').submit(function(event) {
event.preventDefault();
event.stopPropagation();
$.post("#(Url.Action("PdfHeaderAndFooterManager", "Dashboard"))", $(this).serialize(), function(data) {
if (data.success) {
$("#resultMessage").text(data.success);
} else {
$("resultMessage").text(data.success);
}
});
});
});
You are using #using (Html.BeginForm()) and you can't make Ajax call using submit with this,
for ajax call you will have to use: #using (Ajax.BeginForm()),
But you need to upload an image, so you must have to use:
#using (Html.BeginForm("PdfHeaderAndFooterManager", "Dashboard", FormMethod.Post, new { enctype = "multipart/form-data", id = "formPdfImages" }))
So, Ajax call is not possible. For displaying messages on the same page, use another alternative, that may be:
public ActionResult PdfHeaderAndFooterManager(HttpPostedFileBase headerImage, HttpPostedFileBase footerImage)
{
//some code to declare variables
if (headerImage != null)
{
if (!String.IsNullOrEmpty(headerImage.ContentType))
{
headerImageContentType = imageHelper.IsValidImageType(headerImage.ContentType);
if (headerImageContentType)
{
resizedHeaderImage = imageHelper.ResizeImage(headerImage.InputStream);
}
else
{
ViewBag.ReturnMessage="<span style='color:red'> Please Upload an image* file less than 2GB.</span>";
return View();
}
}
}
if (footerImage != null)
{
if (!String.IsNullOrEmpty(footerImage.ContentType))
{
footerImageContentType = imageHelper.IsValidImageType(footerImage.ContentType);
if (footerImageContentType)
{
resizedFooterImage = imageHelper.ResizeImage(footerImage.InputStream);
}
else
{
ViewBag.ReturnMessage="<span style='color:red'>Please Upload an image* file less than 2GB.</span>";
return View();
}
}
}
if (P24DataPrincipal.CurrentIdentity != null)
{
if (resizedHeaderImage != null || resizedFooterImage != null)
{
//add to DB code
ViewBag.ReturnMessage="<span style='color:green'>Image(s) Uploaded Successfully.</span>";
return View();
}
else
{
ViewBag.ReturnMessage="<span style='color:red'>Upload atleast 1 image file.</span>";
return View();
}
}
In View:
#using (Html.BeginForm("PdfHeaderAndFooterManager", "Dashboard", FormMethod.Post, new { enctype = "multipart/form-data", id = "formPdfImages" })){
<div id="resultMessage">
#ViewBag.ReturnMessage
</div>
}