How to update Model from partial after ajax file upload? - c#

So I have ajax file upload partial (using Jquery Form plugin), it's working perfectly, but I don't know to update model value after file uploading
<div>
#Html.Partial("PhotoUpload", Model.Place)
</div>
Here I'm calling partial and giving to it part of a model.
#model PlaceMap.DAL.Entities.Place
#using (Html.BeginForm("PhotoUpload", "Place", FormMethod.Post, new { #id = "photoUpload", enctype = "multipart/form-data" }))
{
{
#Html.ValidationSummary(true, "Image upload was unsuccessful")
#Html.HiddenFor(m => m.Photo)
<input type="file" id="file" name="file"/>
<input type="submit" id="sbm" />
}
}
This is view code of partial, accepting model and form for uploading
var options = {
url: "/Place/PhotoUpload",
dataType: "json",
clearForm: true,
resetForm: true,
success: showResponse
};
function showResponse(responseText, statusText, xhr, $form)
{
$('#photo').append('<img src="/Images/Places/' + responseText.Photo + '" />');
}
$('#photoUpload').submit(function ()
{
$('#photoUpload').ajaxSubmit(options);
return false;
});
Javascript code for plugin
[Authorize]
[HttpPost]
public ActionResult PhotoUpload(string Photo, HttpPostedFileBase file)
{
try
{
using (var ms = new MemoryStream())
{
//some logic here
return Json(new { Photo = filename });
}
}
catch (ArgumentException)
{
}
return PartialView();
}
Controller action code. It's returning file name, it's going to js function "showResponse" and appending image to div. It's all work perfectly, but I have to write file name to #Model.Photo of this partial and I don't know how to do it. Any suggestions?

One possibility is to use text/plain from the server:
return Json(new { Photo = filename }, "text/plain");
and on the client manually parse:
function showResponse(responseText, statusText, xhr, $form) {
var data = $.parseJSON(responseText);
$('#photo').append('<img src="/Images/Places/' + data.Photo + '" />');
}
Obviously you lust remove the dataType: 'json' option for this to work.
Another possibility is to follow what's explained in the documentation and write a custom action result which will wrap your JSON response with the <textarea> tags:
Browsers that support the XMLHttpRequest Level 2 will be able to
upload files seamlessly and even get progress updates as the upload
proceeds. For older browsers, a fallback technology is used which
involves iframes since it is not possible to upload files using the
level 1 implmenentation of the XMLHttpRequest object. This is a common
fallback technique, but it has inherent limitations. The iframe
element is used as the target of the form's submit operation which
means that the server response is written to the iframe. This is fine
if the response type is HTML or XML, but doesn't work as well if the
response type is script or JSON, both of which often contain
characters that need to be repesented using entity references when
found in HTML markup.
To account for the challenges of script and JSON responses when using
the iframe mode, the Form Plugin allows these responses to be embedded
in a textarea element and it is recommended that you do so for these
response types when used in conjuction with file uploads and older
browsers. Please note, however, that if there is no file input in the
form then the request uses normal XHR to submit the form (not an
iframe). This puts the burden on your server code to know when to use
a textarea and when not to. If you like, you can use the iframe option
of the plugin to force it to always use an iframe mode and then your
server can always embed the response in a textarea. But the
recommended solution is to test for the 'X-Requested-With' request
header. If the value of that header is 'XMLHttpRequest' then you know
that the form was posted via ajax.

Related

Return PDF file to user after ajax call to controller

I have a custom button on my Kendo Grid "Export to PDF". On click of this button, I call a service which generates a PDF file.I want to be able to display this PDF file on click of the "Export to PDF" button. I have it working to the point where it calls the service and I save the PDF locally on my machine as of now. How can I return this file to the View so that its either displayed on a new tab or maybe just a dialog which asks the user to open and save the save.
The following is the button click method which make the call to the controller:
<script>
function ExportDocGen_Click() {
var grid = $("#companyMasterRateSheets").data("kendoGrid");
var selectedItem = grid.dataItem(grid.select());
var orderQuoteId = selectedItem.QuoteID;
$.ajax({
url: '#Url.Action("GenerateRateSheetPDF", "AccountDetail")',
type: 'GET',
dataType: 'string',
cache: false,
data: { 'orderQuoteId': orderQuoteId },
success: function (color) {
return color;
},
error: function () {
alert('Error occured');
}
})
}
</script>
The controller is as follows:
[HttpGet]
public string GenerateRateSheetPDF(string orderQuoteId)
{
byte[] pdfData = ServiceManager.BuildPDF(Convert.ToInt32(orderQuoteId));
if (pdfData != null)
{
BinaryFormatter bf = new BinaryFormatter();
MemoryStream ms = new MemoryStream();
bf.Serialize(ms, pdfData);
System.IO.File.WriteAllBytes("hello.pdf", pdfData);
}
return ("hi");
}
I am returning string as of now since I am not sure what should the return type will be. Thanks for the help.
Because theres no people answer/comment this question yet. I think i need to give you help to do this thing generally (all language)
I dont know about .net and kendo but i will give you live demo and simple explaination. My code written in PHP but i will try to explain this generally, because this is the common question for any web developer
There is two way to doing this:
Save file to storage then give user the produced link
Ajax
$.ajax({
url: '/generatePDF',
success: function(d){
document.location = d;
}
})
controller (server side)
var pdf = createPDF();
saveToServer(pdf, 'path/filename.pdf');
this will give user pdf real path. (i think you dont want this in your case)
create file on the fly
Html
description
controller (server side)
var pdf = createPDF();
addHeader('Content-Disposition: attachment or inline');
printToScreen(pdf);
pdf choice:
replace active tab
open new tab
open new window
embed
force download (client storage)
save locally (server storage)
1. Replace Active Tab
Basic HTML: description
Javascript /jquery window.open(url, '_self'); 2nd param must be _self or _parent
Requirement: Content-Disposition is not attachment
2. Open New Tab
Basic HTML: description depend on user preference setting
Javascript /jquery window.open(url, '_blank'); without 3rd parameter
Requirement: Content-Disposition is not attachment
3. Open New Window
Basic HTML: description depend on user preference setting
Javascript /jquery window.open(url, '_blank', 'width=200'); with 3rd parameter
Requirement: Content-Disposition is not attachment
4. Embed
Basic HTML: <embed src="url"></embed>
javascript/jquery: $(selector).html('<embed src="url"></embed>');
Requirement: no matter Content-Disposition is attachment or not, browser always try to display it
5. Force Download (client storage)
Basic HTML: `
Javascript /jquery: document.location = url;
Requirement: Content-Disposition is must be attachment
6. Save locally (server storage)
I will not explain this because:
- you already doing this
- And every language have different syntax
Example Implementation
index.html (html + js)
<!--
basic html: depend on user browser preference setting to open as new window or new tab
-->
<button id="newtab1">new tab/new window: basic html</button>
<!--
force to open new tab
case: without ajax
-->
<button id="newtab2">new tab: jquery without ajax</button>
<!--
force to open new tab
case: with ajax
hint:
* window.open() outside ajax will NOT BLOCKED
* window.open() inside ajax will BLOCKED
* so we need to hack this
* 1st: prepare/declare new var to store blank window
* 2nd: change location.href to execute it inside ajax
-->
<button id="newtab3">new tab: jquery with ajax</button>
<!--
force to open new window
case: without ajax
-->
<button id="newwindow1">new window: jquery without ajax</button>
<!--
force to open new window
case: with ajax
-->
<button id="newwindow2">new window: jquery with ajax</button>
<!--
embed
-->
<button id="embed">embed</button>
<!--
the most easy way
force download using HTML5 anchor download attribute
maybe not work for old browser
hint:
- no download attribute: display pdf
- download attribute exists, but content-disposition header is not set as attachment: display pdf
- download attribute exists, but content-disposition header is set as attachment: download pdf
-->
<button id="forcedownload1">force download using anchor download attribute (fail)</button>
<button id="forcedownload1">force download using anchor download attribute (correct)</button>
<!--
force download using ajax
i think this work in old browser too, since jquery take care of them
-->
<button id="forcedownload2">force download 2 using ajax/jquery</button>
<hr>
<div id="preview-embed"></div>
<script src="jquery.min.js"></script>
<script>
$(function(){
/*
* you need to read window.open() first: https://www.w3schools.com/jsref/met_win_open.asp to understand the basic
* hint:
* window.open() recieve 4 parameter
* if we not set 3rd parameter: will force open new tab
* if we set 3rd parameter: will force open new window
*/
$('#newtab2').click(function(){
/*
* no matter content-disposition is attachment or not, it always display in browser
*/
window.open('http://creativecoder.xyz/stackoverflow/pdf-choice/createpdf.php?filename=dummy.pdf');
});
$('#newtab3').click(function(){
var newWindow = window.open("","window name");
$.ajax({
url: 'http://creativecoder.xyz/stackoverflow/pdf-choice/givemethelink.php',
success: function(d){
newWindow.location.href = d;
},
error: function(d){
alert('error');
}
});
});
$('#newwindow1').click(function(){
window.open('http://creativecoder.xyz/stackoverflow/pdf-choice/createpdf.php?filename=dummy.pdf','window name', 'width=200,height=100');
});
$('#newwindow2').click(function(){
var newWindow = window.open("","window name", 'width=200,height=100');
$.ajax({
url: 'http://creativecoder.xyz/stackoverflow/pdf-choice/givemethelink.php',
success: function(d){
newWindow.location.href = d;
},
error: function(d){
alert('error');
}
});
});
$('#embed').click(function(){
$('#preview-embed').html('<embed src="http://creativecoder.xyz/stackoverflow/pdf-choice/createpdf.php?file=dummy.pdf"></embed>');
});
$('#forcedownload2').click(function(){
$.ajax({
/*
* we need to get file with header attachment
* if our file is dont have Content-Disposition: attachment , this ajax will display pdf only
* so we need to set request parameter `force=1`
*/
url: 'http://creativecoder.xyz/stackoverflow/pdf-choice/givemethelink.php?force=1', //correct
//url: 'http://creativecoder.xyz/stackoverflow/pdf-choice/givemethelink.php', //fail
success: function(d){
document.location = d;
},
error: function(d){
alert('error');
}
});
});
});
</script>
server scripting
server script example is written in PHP, please find the match syntax using your own language.
here i will implement method 2 only. i have so much different case. But i will choose the case which we have 2 script: 1. for give the link 2. generate pdf
givemethelink.php (only give url path)
<?php
if(isset($_GET['force']) && $_GET['force'] ==1){ //if request GET parameter force exist and the value is 1
echo 'http://creativecoder.xyz/stackoverflow/pdf-choice/createpdf.php?filename=dummy.pdf&force=1';
}else{
echo 'http://creativecoder.xyz/stackoverflow/pdf-choice/createpdf.php?filename=dummy.pdf';
}
createpdf.php (generate pdf on the fly /method 2)
<?php
/*
* generate pdf on the fly here
* but here, i only use file_get_contents() to download existing pdf binary
* and then simply display it to the screen using echo
* because it can difficult to you to understand, if i give you real php generate pdf script
*/
$source = file_get_contents('dummy.pdf'); //download existing pdf. this line should be pdf generate script
/*
* set header. this is the key
* the main header requirement is Content-Disposition
* you still can set any other header which is optional
*/
header("Content-type: application/pdf"); //tell browser: this is pdf file (optional header example)
/*
* if is request GET force exists and the value is 1 : force download
* else display to the browser
*/
if(isset($_GET['force']) && $_GET['force'] ==1){
header("Content-Disposition: attachment; filename=newname.pdf"); //tell browser: download this
}else{
header("Content-Disposition: inline; filename=newname.pdf"); //tell browser: display this file in browser
}
echo $source; //print pdf source to screen
force=1 => give attachment header
else => give inline header
Here the live demo: http://creativecoder.xyz/stackoverflow/pdf-choice/index.html
Conclusion:
Create script which genereate pdf on the fly not the real/direct pdf path
set header content-disposition: attachment or not, each have the different behaviour
bonus example: try run this code
test.html
display 1
download 1
<hr>
display 2
download 2

Kendo UI Uploader Async Cross Site Scripting Issue with ASP.NET

I have attempted to use the Kendo UI Uploader in async mode in an ASP.NET environment, using very similar code to the demo's on there site and am getting what appears to be a JQuery cross site scripting error on both IE8 and Chrome 30.0.1599.101 m
The following is the code being used:
index.chtml
<!DOCTYPE html>
<html>
<head>
<title></title>
<link href="css/kendostyles/kendo.common.min.css" rel="stylesheet" />
<link href="css/kendostyles/kendo.default.min.css" rel="stylesheet" />
<script src="scripts/kendojs/jquery.min.js"></script>
<script src="scripts/kendojs/kendo.all.min.js"></script>
</head>
<body>
<div style="width:45%">
<div style="width:45%">
<div class="demo-section">
<input name="files" id="files" type="file" />
</div>
</div>
<script>
$(document).ready(function () {
$("#files").kendoUpload({
async: {
saveUrl: "save",
removeUrl: "remove",
autoUpload: true
}
});
});
</script>
</div>
</body>
</html>
I have extremely simple upload code that is not even being hit, but i will provide it for completeness:
public ActionResult Save(IEnumerable<HttpPostedFileBase> files)
{
// The Name of the Upload component is "files"
if (files != null)
{
foreach (var file in files)
{
// Some browsers send file names with full path.
// We are only interested in the file name.
var fileName = Path.GetFileName(file.FileName);
var physicalPath = Path.Combine(Server.MapPath("~/Uploads"), fileName);
// The files are not actually saved in this demo
// file.SaveAs(physicalPath);
}
}
// Return an empty string to signify success
return View();
}
Now upon selecting a file to be uploaded i receive the following javascript pop up message:
Error! Upload failed.Unexpected server response - see console.
Upon viewing the console i get this:
Server response: Error trying to get server response: SecurityError: Blocked a frame with origin "http://localhost:21739" from accessing a frame with origin "null". The frame requesting access has a protocol of "http", the frame being accessed has a protocol of "data". Protocols must match.
in an attempt to resolve the issue after searching around for like issues i have tried bypassing the issue with changing the global.asax file
protected void Application_BeginRequest(object sender, EventArgs e)
{
HttpContext.Current.Response.AddHeader("Access-Control-Allow-Origin", "*");
}
But that had no effect.
What is the way to get the Async Kendo File Upload working when presented with this sort of error, i have been unable to find anyone suffering from the same situation?
A couple of things:
1) chrome is explicit about XSS problems. Unless chrome console says cross site scripting error, then I don't think that's your issue.
2) Add yourself a call back for when it errors out.
$("#files").kendoUpload({
async: {
saveUrl: "save",
removeUrl: "remove",
autoUpload: true
},
error: onError
});
function onError(e){
alert("Failed to upload " + e.files.length + " files " + e.XMLHttpRequest.status + " " + e.XMLHttpRequest.responseText);
}
This will give you the response from the server.
3) Not sure if you are using Asp.net MVC or Web Api controllers (looks like ASP.Net MVC), but (maybe I'm wrong) it seems your save property needs the controller or route details.
$("#files").kendoUpload({
async: {
saveUrl: "MyControllerName/save", (or api/controller) ( or #Url.Action("Save", "ControllerName")
removeUrl: "remove",
autoUpload: true
}
});
4) returning KendoUI upload isn't going to like the return type ActionResult It requires a JsonResult return type. correction : Not 100% sure on this. Just be careful of how the return data is structured. If it's a string with well-formed HTML, the control will bark

IE wants to download JSON result....MVC3

My MVC3 app uploads documents from the user to our server. I am returning a JsonResult to display any errors, if any:
[HttpPost]
public JsonResult SaveDocument(DocumentModel model, HttpPostedFileBase postedFile)
{
//my wonderful code
return Json(new { success = true, message="ok" });
}
Heres how i submit the request:
var isSubmitting = false;
var addDocumentOptions = {
beforeSubmit: beforeAddDocumentSubmit, // pre-submit callback
success: afterDocumentSubmit // post-submit callback
};
$('#btnCreateDocument').click(function (e) {
e.preventDefault();
$('#divError').html('');
if (!isSubmitting) {
$('#createDocForm').submit();
}
});
This javascript function runs when the upload is complete:
function afterDocumentSubmit(responseText, statusText, xhr, $form) {
if (responseText.success) {
//no errors
} else {
$('#divError').html('Error: ' + responseText.message);
}
}
In FF, Chrome etc., my javascript code runs fine, but in IE, the browser wants to download the Json result as text. I get a download/open file dialog box that shouldnt appear. How do i make IE not download my Json result and behave like the other browsers? Thanks
I ran into a similar problem doing the same in Spring MVC on Java. The problem was that Spring was returning the content-type of the JSON result as application/json, which seems to make IE want to download it. You can try changing the content-type to text/plain; IE won't prompt you to download the file in this case. I suspect that something similar might be happening here.
You could try:
return Json(new { success = true, message = "ok" }, "text/plain");
In response to your new problem: the issue is that responseText is just a string. What you need to do is to convert it into a Javascript Object. You can do it like this:
var response = JSON.parse(responseText);
if(response.success) {
...
}
Most browsers support JSON.parse(). If you're having issues with non-compliant browsers, you can always use the JSON Javascript Library.
Wild guess: you are using the jquery.form plugin which enables you to upload files using AJAX and you haven't read the documentation which states the following:
Browsers that support the XMLHttpRequest Level 2 will be able to
upload files seamlessly and even get progress updates as the upload
proceeds. For older browsers, a fallback technology is used which
involves iframes since it is not possible to upload files using the
level 1 implmenentation of the XMLHttpRequest object. This is a common
fallback technique, but it has inherent limitations. The iframe
element is used as the target of the form's submit operation which
means that the server response is written to the iframe. This is fine
if the response type is HTML or XML, but doesn't work as well if the
response type is script or JSON, both of which often contain
characters that need to be repesented using entity references when
found in HTML markup.
To account for the challenges of script and JSON responses when using
the iframe mode, the Form Plugin allows these responses to be embedded
in a textarea element and it is recommended that you do so for these
response types when used in conjuction with file uploads and older
browsers. Please note, however, that if there is no file input in the
form then the request uses normal XHR to submit the form (not an
iframe). This puts the burden on your server code to know when to use
a textarea and when not to.
Now that you have read it you should take the respective actions if you want your code to work under IE as I have exemplified in this post.
The proper JsonResult return should look like so:
[HttpPost]
public JsonResult SaveDocument(DocumentModel model, HttpPostedFileBase postedFile)
{
...
return Json(new { success = true, message="ok" }, "application/json; charset=utf-8", JsonRequestBehavior.AllowGet);
}

Using .ajaxForm and C# MVC, how can I prevent Internet Explorer from trying to save Json result?

I'm returning some Json via a C# MVC Controller. Other browsers work fine, but Internet Explorer (IE9) tries to Save the returned Json data as a Text file. Any ideas how to keep this from happening?
//MVC Controller Code...
return Json(new { redirectToUrl = Url.Action("Create", "Album",
new { url = url, isLocalFile = isLocalFile})});
//Clientside Javascript Code
$("#uploadImageForm").ajaxForm({
beforeSubmit: function () {
},
success: function (data, textStatus, xhr) {
window.location.href = data.redirectToUrl;
},
error: function (data, textStatus, xhr) {
}
});
I've tried adding "text/plain" and "text/json" to the second argument of the return Json method, it doesn't work.
Many thanks!
Quote from the documentation of the jquery.form plugin:
Browsers that support the XMLHttpRequest Level 2 will be able to
upload files seamlessly and even get progress updates as the upload
proceeds. For older browsers, a fallback technology is used which
involves iframes since it is not possible to upload files using the
level 1 implmenentation of the XMLHttpRequest object. This is a common
fallback technique, but it has inherent limitations. The iframe
element is used as the target of the form's submit operation which
means that the server response is written to the iframe. This is fine
if the response type is HTML or XML, but doesn't work as well if the
response type is script or JSON, both of which often contain
characters that need to be repesented using entity references when
found in HTML markup.
To account for the challenges of script and JSON responses when using
the iframe mode, the Form Plugin allows these responses to be embedded
in a textarea element and it is recommended that you do so for these
response types when used in conjuction with file uploads and older
browsers. Please note, however, that if there is no file input in the
form then the request uses normal XHR to submit the form (not an
iframe). This puts the burden on your server code to know when to use
a textarea and when not to.
This means that if your form contains file input fields and you are submitting this form to a controller action that returns JSON, you must wrap this JSON in a <textarea> tags.
So your response should not look like this:
{ "redirectToUrl":"some url" }
it should look like this:
<textarea>{ "redirectToUrl":"some url" }</textarea>
In order to achieve that you could use a custom action result that will wrap the response with those tags:
public class TextareaJsonResult : JsonResult
{
public TextareaJsonResult(object data)
{
this.Data = data;
}
public override void ExecuteResult(ControllerContext context)
{
var response = context.HttpContext.Response;
bool shouldWrap = !context.HttpContext.Request.IsAjaxRequest();
if (shouldWrap)
{
response.Write("<textarea>");
}
base.ExecuteResult(context);
if (shouldWrap)
{
response.ContentType = "text/html";
response.Write("</textarea>");
}
}
}
and then have your controller action return this custom result:
[HttpPost]
public ActionResult Upload(HttpPostedFileBase file)
{
// ... some processing
var redirectToUrl = Url.Action(
"Create",
"Album",
new { url = url, isLocalFile = isLocalFile }
);
return new TextareaJsonResult(new { redirectToUrl = redirectToUrl });
}
Now obviously in your AJAX success callback you also need to account for this difference by testing the typeof result and in the case of a legacy browser (such as Internet Explorer) manually parse the response. You may take a look at the source code of the page I have linked to.
But this being said, I can see that in your success callback you are redirecting to a controller action contained in the JSON response returned by the server. Here comes my question: What's the point of using AJAX in the first place if you are going to redirect? Why don't you use a standard form post to the controller action and have the controller action directly perform the redirect? AJAX should be used when you want to stay on the same page.
I agree with Jesse's comment, this is probably a duplicate of the link he provided.
As such I'll provide an alternative. I prefer to view json coming over the wire using a http proxy similar to fiddler http://www.fiddler2.com/fiddler2/ . I mention fiddler because it works with all browsers. The advantage is that you get a treeview of the parsed json. It's MUCH easier to read and find what you're looking for.
Also, I believe firebug for firefox, chrome dev tools, and ie dev tool all provide the same functionality. (I know chrome has the treeview, I think I remember firebug having it, and I would be surprised if modern IE doesn't have it considering the other 2 do.)

MVC dynamic JQuery recieve dropdown value to feed HTML Helper that returns an image

Questions;
6164507
7869467 &
3040342 have been very helpful BUT....
In .NET MVC C#
I am trying to harvest a value from a dropdown and pass that value to my html helper which renders an
image in a div. The questions cited above got me as far as this JdFiddle
http://jsfiddle.net/JSyLV/276/
I'm pretty sure I can't place the selected option value in a var and pass it to the HTML Helper.
( different languages in the same viewpage = Krazy !!)
So I guess all I''m asking is how to get the value to the HTML Helper.
I'm confident that I can scratch together the HTML Helper to return the image/anchor thingie.
$(document).ready(function ()
{
$('#selectMe').change(function ()
{
alert("changefunction called");
alert('#'+$(this).val());
alert($(this).val());
})
});
You can use an AJAX request to get the image information from the server:
$(function () {
$('#selectMe').change(function () {
$.ajax({
url : '<url-of-html-helper>',
type : 'get',
data : { selectMeVal : $(this).val() },
success : function (serverResponse) {
//if you server response is the HTML of the image then you can just add the serverResponse to the DOM
$('#some-container').html(serverResponse);
},
error : function (jqXHR, errorText, c) {
//if an error occurs, handle it here, like re-run the request, or show a message to the user
}
});
})
});
If instead of outputting the HTML for the image in your server-side code, you could just pass a source for the image and update an image already in the DOM (or create a new one via JavaScript). I generally try to limit the data that comes in via AJAX requests (it's always good to have your responses be less than 1500 bytes so it fits in a single packet, most of the time).

Categories