C# generating and downloading PDF file not working - javascript

I'm trying to generate and then download PDF file from a simple form.
I've got 2 options for users:
1. Generate and download directly after filling the form
2. Generate and download the saved documents.
The first one works perfectly, but the second case has a problem.
I'm using ajax to call the Download action:
function complaintDownload(object) {
var id = object.id;
$.ajax({
type: "POST",
url: '/Lawyers.AppPortal/Statement/Download',
data: { id: id}
});
}
Here's my action:
[HttpPost]
public FileContentResult Download(int id)
{
var statement = Engine.Get<Statement>().FirstOrDefault(a => a.Id == id);
var pdfModel = statement.ToStatementPdfModel();
var path = BLL.PdfGenerator.Generate(pdfModel);
var pdfBytes = System.IO.File.ReadAllBytes(path);
return File(pdfBytes, "Application/pdf", "test.pdf");
}
When I call this action nothing happens.

Related

How to post file with ajax?

Trying to save a file to a db. I am using formData via javascript to append the file and adding this as a post object via ajax. for some reason nothing gets sent.
What am I doing wrong?
HTML
<input type="file" style="display: none;" class="btn btn-primary uploadFile">
script:
$(".saveImage")
.on("click",
function() {
var files = $(".uploadFile");
var data = new FormData();
data = $.OverWatch.worker.uploadFileHandler.addUploadFiles(files, data);
$.OverWatch.worker.postUserData("/Administration/AddUserImage", data, function () {
alert("done");
});
});
Functions above look like:
addUploadFiles: function (files, data) {
$.each(files, function (i, v) {
var file = $(this).data("files");
data.append("file", file);
});
return data;
}
postUserData:
postUserData: function(url, data, callback) {
$.LoadingOverlay("show");
$.ajax({
url: url,
type: 'POST',
data: data,
cache: false,
processData: false,
contentType: false,
dataType: "HTML",
success: function(data) {
if (callback) {
callback(data);
$.LoadingOverlay("hide");
}
},
error: function(event, jqxhr, settings, thrownError) {
//$.helpers.errorHandler($("#fileDialogErrors"), event.responseText);
var h;
$.LoadingOverlay("hide");
}
});
},
backend:
public ActionResult AddUserImage()
{
if (Request.Files.Count != 0)
{
//save
}
return null;
}
edit:
var files = $(".uploadFile");
returns:
Your var file = $(this).data("files"); line of code would be returning undefined (unless you have some other javascript adding a data value, but you cannot add files to data so it in any case it would not be returning a file).
Change your loop to
$.each(files, function (i, v) {
for (i = 0; i < v.files.length; i++) {
var file = v.files[i];
data.append("file", file);
}
});
However, you can simplify this by using var data = new FormData($('form').get(0)); which will serialize all you form controls including file inputs to FormData (refer how to append whole set of model to formdata and obtain it in MVC for more information).
I also recommend you change your method signature to
public ActionResult AddUserImage(IEnumerable<HttpPostedFileBase> files)
and let the DefaultModelBinder do its magic.
you can directly get file from controller when called using Request.Files
//(Request) HttpRequestBase object for the current HTTP request
if (Request.Files.Count > 0)//// Is image is uplaod by browse button
{
var inputStream = Request.Files[0].InputStream;
using (var binaryReader = new BinaryReader(inputStream))
{
var ImageBytes = binaryReader .ReadBytes(Request.Files[0].ContentLength); // same as you can get multiple file also
}
var fileExtension = Path.GetExtension(Request.Files[0].FileName);
}
thanks.
I haven't done it with jQuery but just learned how to do it myself yesterday using plain old javascript... the following worked for me. If you want to stick with jquery maybe you can translate the functions to what you need:
var formElement = document.querySelector("form");
var payload = new FormData(formElement);
function onStateChange(ev) {
// Check if the request is finished
if (ev.target.readyState == 4) {
editor.busy(false);
if (ev.target.status == '200') {
// Save was successful, notify the user with a flash
} else {
// Save failed, notify the user with a flash
}
}
};
xhr = new XMLHttpRequest();
xhr.addEventListener('readystatechange', onStateChange);
xhr.open('POST', '/posts');
xhr.send(payload);
Maybe see if using the above code works for you (it just targets a form that you have on the same page), and then you can troubleshoot whether it's your script that's the problem or a backend / communication problem.

passing php variables with ajax

I've been trying to get php to rename an image in an S3 bucket. There's three stages
upload image (you can see a previous question I asked to see my
solution to that) I've included the code I used below.
Use ajax to take the name I want for a file (the user ID) and
pass it to a PHP renaming script.
run the renaming script.
Step two is giving me problems. If i hard code the names into the PHP script then it will find the source file and rename it.
However I can't get it to rename the files using variables drawn from the page.
Here's the HTML button code
<input type="file" id="file-chooser" />
<button onclick= "pass()" id="upload-button">Upload to S3</button>
<div id="results"></div>
Here's the JS code
<script type="text/javascript">
var bucket = new AWS.S3({params: {Bucket: 'MY BUCKET'}});
var fileChooser = document.getElementById('file-chooser');
var button = document.getElementById('upload-button');
var results = document.getElementById('results');
button.addEventListener('click', function() {
var file = fileChooser.files[0];
if (file) {
results.innerHTML = '';
var filename = file.name;
var params = {Key: file.name, ContentType: file.type, Body: file};
bucket.upload(params, function (err, data) {
results.innerHTML = err ? 'ERROR!' : 'UPLOADED';
ajax_post();
});
} else {
results.innerHTML = 'Nothing to upload.';
}
}, false);
function pass() {
$.get("test.php");
return false;
}
function ajax_post() {
var ref = new
Firebase("https://MY FIREBASE .firebaseio.com/");
var authData = ref.getAuth();
// Create our XMLHttpRequest object
var fileChooser = document.getElementById('file-chooser');
var file = fileChooser.files[0];
var filename = file.name;
var userID = authData.uid;
var userID = USERS USER ID;
//alert($(this).attr('id'));
$.ajax({
type: "POST",
url: 'test.php',
data: { userID : userID },
data: { filename : filename },
success: function (data) {
alert("success!");
}
};
pass();
}
</script>
Here's the PHP.
<?php
require 'vendor/autoload.php';
use Aws\S3\S3Client;
$sourceBucket = "MY BUCKET";
$sourcename1 = $_POST['filename'];
$targetKeyname = $_POST['userID'];
$targetBucket = "MY BUCKET";
$s3 = S3Client::factory(array(
'key' => "MY KEY",
'secret' => "MY SECRET KEY"
));
$s3->copyObject(array(
'Bucket' => $targetBucket,
'Key' => $targetKeyname,
'CopySource' => "{$sourceBucket}/{$sourcename}",
));
?>
EDITING TO ADD
I've been running test after test. If I hard code the variables into the PHP file it works. If I hard code the variables into the JS script it fails. The ajax is running the php file it's just not passing the variables to it. I've tried with and without the ISSET on the PHP side it just fails to take in the variables each time.
Any ideas?
I suspect this is your issue var userID = USERS USER ID; I'm not sure where that information is coming from. But without seeing the html/js where that is derived from, its difficult to determine the problem.
If its text input with an id of userID, it should be something like:
<input type="text" name="userID" id="userID">
js:
var userID = $("#userID").val();
AJAX Update
merge BOTH data into one object:
data: { userID : userID },
data: { filename : filename },
will become
data: { userID : userID , filename : filename },
I found out what the issue was. It was Firebase. Firebase was returning the values I required however the PHP script was retrieving an error from AWS. I had assumed that the AJAX was failing to pass the value on but I was only half right. the AJAX failed because it didn't have the value at that point. I added in a 3 second delay before the upload occurs and it works fine. Firebase was just slower than the PHP.

Handling FileResult from JQuery Ajax

I have a MVC C# Controller that returns a FileResult
[HttpPost]
public FileResult FinaliseQuote(string quote)
{
var finalisedQuote = JsonConvert.DeserializeObject<FinalisedQuote>(System.Uri.UnescapeDataString(quote));
return File(finalisedQuote.ConvertToPdf(), "application/pdf");
}
Now I want to be able to download the result (I don't want it to open up in the browser)
I am calling the controller via $.ajax method in JavaScript
var postData = { quote: finalisedQuote };
var url = "/NewQuote/FinaliseQuote";
$.ajax({
type: "POST",
url: url,
data: postData,
success: function (data) {
//how do I handle data to force download
},
fail: function (msg) {
alert(msg)
},
datatype: "json"
});
How do I handle the data to force it to download?
You won't be able to use JavaScript to save the file to disk as this is blocked for security reasons.
An alternative would be to save the file in the FinaliseQuote action method (and return just an id for the file), then create another action method that responds to a GET request and returns the file. In your success function you then set window.location.href to point to your new action method (you'll need to pass an id for the file). Also make sure you set the MIME type to application/octet-stream and that should force the browser to download the file.
Controller:
[HttpPost]
public JsonResult FinaliseQuote(string quote)
{
var finalisedQuote = JsonConvert.DeserializeObject<FinalisedQuote>(System.Uri.UnescapeDataString(quote));
// save the file and return an id...
}
public FileResult DownloadFile(int id)
{
var fs = System.IO.File.OpenRead(Server.MapPath(string.Format("~/Content/file{0}.pdf", id)));
// this is needed for IE to save the file instead of opening it
HttpContext.Response.Headers.Add("Content-Disposition", "attachment; filename=\"filename\"");
return File(fs, "application/octet-stream");
}
JS:
success: function (data) {
window.location.href = "/NewQuote/DownloadFile?id=" + data;
},

Add data to Jquery.Ajax post when uploading file?

I'm building a drag n drop upload feature for my MVC 4 webapp using this tutorial:
http://hayageek.com/drag-and-drop-file-upload-jquery/
However, besides the file, I also want to send info regarding where it came from (some ID for instance) and the type (domain-type).
In my ajax post below, how can I add extra data and read it server side?
In the tutorial code a line with 'extra data' is seen, however it is not actually implemented.
function sendFileToServer(formData, status) {
var uploadURL = '/home/upload';
var extraData = {}; //Extra Data.
var jqXHR = $.ajax({
xhr: function() {
var xhrobj = $.ajaxSettings.xhr();
if (xhrobj.upload) {
xhrobj.upload.addEventListener('progress', function(event) {
var percent = 0;
var position = event.loaded || event.position;
var total = event.total;
if (event.lengthComputable) {
percent = Math.ceil(position / total * 100);
}
//Set progress
status.setProgress(percent);
}, false);
}
return xhrobj;
},
url: uploadURL,
type: "POST",
contentType: false,
processData: false,
cache: false,
data: formData,
success: function (data) {
status.setProgress(100);
//$("#status1").append("File upload Done<br>");
}
});
status.setAbort(jqXHR);
}
Thanks!
To pass additional data to your controller via Ajax Upload File, it just needs to append your additional data to formData in your Ajax call function(sendFileToServer). i.e. you'd like to pass an upload target folder:
formData.append('targetFolder', 'User File Upload Folder');
and in your action get access to your additional parameter:
[HttpPost]
public ActionResult Upload(HttpPostedFileBase file, string targetFolder)
I fixed the problem by creating a workaround, I'm passing the extra data (id etc) via the URL which I receive as parameters on the controller method.

Can't send javascript object array to new page

I have a javascript array of objects that I am trying to send to my new view. I don't want to use AJAX - this is a new page altogether. I can't seem to figure out how to send the array to my controller action.
I have an action that returns an UploadFile object to my view, which is added to an array in javascript. When the user tries to continue to "review" the results, I'm sending that array of objects to the new page.
public ActionResult Review(List<UploadFile> model)
{
return View();
}
I still tried using AJAX but I encountered two problems: 1) I want a new page, I don't want it to stay on the same page, 2) The model is still null.
$('.js_btn-review').click(function () {
$.ajax({
url: '/Document/Review',
data: documents,
type: 'GET'
});
});
I'm not sure how to do this - I know I've done it before, but I can't remember how I did it. I even tried setting an element and serializing:
$('.js_btn-review').click(function () {
$("#documents").val(documents);
$.ajax({
url: '/Document/Review',
data: $("#documents").serialize(),
type: 'GET'
});
});
What am I doing wrong here?
As you didn't state the structure of the js variable documents I am assuming it is just an array of UploadFiles. Something along the lines of:
var documents = [];
for (var i = 0; i < 10; i++) {
var d = {
id: i,
title: i.toString()
};
documents.push(d);
}
Which you then taking and sending via ajax. This will not work as documents do not have a container so the URL for the GET will look something like /Document/Review?undefined=&undefined=&undefined=&undefined=&
By changing it to
var data = {
model: documents
};
$('.js_btn-review').click(function () {
$.ajax({
url: '/Home/Review',
data: data,
type: 'GET'
});
});
The models should be populated.
As for the view changing you will need to do something other than .ajax as just goes and gets the response. it doesn't change pages.
The parameter name should be the same as the name of the data being sent, so if the data object is called documents, your parameter in the controller should have the same name as follows.
public ActionResult Review(List<UploadFile> documents)
{
return View();
}
the Javascript for that
$('.js_btn-review').click(function () {
$.ajax({
url: '/Document/Review',
data: JSON.stringify( documents ),
type: 'Post'
});
});
Without AJAX
It's just like the PRG Pattern so you POST a form.
#{
List<UploadFile> list = new List<UploadFile> { ... };
}
#using(Html.BeginForm("Review", "Controller", FormMethod.Post, new { }))
{
for (int i = 0; i < list.Count(); i++)
{
#Html.HiddenFor(m => list[i]);
}
<input type="submit" value="Review" />
}
Controller Actions
[HttpPost]
public ActionResult Review(List<UploadFile> list)
{
TempData["Review_list"] = list;
return RedirectToAction("Review");
}
[HttpGet]
public ActionResult Review()
{
var list = TempData["Review_list] as List<UploadFile>;
return View(list);
}
If you don't need to process the list on the server
Don't bother with passing back and keep the list in javascript:
Store it in Web Storage like sessionStorage as JSON
Do a regular GET to navigate to your new page (e.g. Review)
Retrieve the list from sessionStorage and build the html

Categories