MVC CSV File Download + Project Awesome - asp.net-mvc-3

I'm currently using project awesome to pop a form which asks for an account number.
I take the number and generate a csv file and send it to the browser:
string billcsv = "account_ref,line1,line2,line3"
var VIRFile = File(new System.Text.UTF8Encoding().GetBytes(billcsv), "text/csv", "billfor" + accnumber+ ".csv")
return Json(VIRFile);
I would like the end user to prompted to save the csv file but cannot figure out who to.
Should I create the CSV file on disk first then pass the url to the file to the success function and use window.open(url) or is it possible to use javascript to recreate the file from the json result?
Json Result:
{"FileContents":[65,99,99,111,117,110,116,95,82,69,70,44,73,78,86,79,73,67,69,95,84,89,80,69,44,73,78,86,79,73,67,69,95,82,69,70,44,81,84,89,95,79,82,68,69,82,44,83,69,82,86,73,67,69,95,84,69,88,84,44,85,78,73,84,95,80,82,73,67,69,44,83,69,82,86,73,67,69,95,65,77,79,85,78,84,13,10,114,114,114,44,73,110,118,111,105,99,101,44,86,73,82,49,48,50,44,49,44,83,116,97,114,83,104,105,112,32,32,79,110,101,13,10,44,76,79,65,32,45,32,32,109,116,114,115,13,10,44,71,82,84,32,45,71,84,44,48,44,48,44,48,13,10,114,114,114,44,73,110,118,111,105,99,101,44,86,73,82,49,48,50,44,50,44,66,111,97,116,32,84,114,97,110,115,102,101,114,115,32,72,105,114,101,32,67,104,97,114,103,101,44,50,53,48,46,48,48,44,53,48,48,46,48,48,13,10,114,114,114,44,73,110,118,111,105,99,101,44,86,73,82,49,48,50,44,51,44,66,101,114,116,104,105,110,103,32,32,82,70,65,32,47,32,77,111,68,44,51,53,48,46,48,48,44,49,48,53,48,46,48,48,13,10],"ContentType":"text/csv","FileDownloadName":"billfor123.csv"}

First of all don't use AJAX for downloading files. Use a normal form submission or an anchor pointing to the controller action which will serve the file. And then in the controller:
public ActionResult Download(string accnumber)
{
string billcsv = "account_ref,line1,line2,line3";
var data = Encoding.UTF8.GetBytes(billcsv);
string filename = "billfor" + accnumber + ".csv";
return File(data, "text/csv", filename);
}
Now in order to invoke this action and have the user be prompted for download simply create a link:
#Html.ActionLink("Download csv", "Download", new { accnumber = "123" })
or if you are using a form:
#Html.BeginForm("Download", "SomeController")
{
#Html.TextBox("accnumber")
<input type="submit" value="Download CSV" />
}

Submitting the form using AJAX has disadvantages. You shouldn't be doing this. I would rather use #Html.BeginForm() helper to generate the form and submit the data using button event.
Hope this helps

Related

How to upload file via ajax

I have file upload which doesn't use form to upload the file instead I want to upload it using ajax. I tried the following approach but I cannot pass the file. It's null. Please help. Below is my implementation.
HTML and jQuery function
<div id="Upload">
<input type="file" accept="application/x-shockwave-flash" id="virtualtourfile" enctype="multipart/form-data"/>
<input type="button" value="Upload" id="btnUpload"/>
</div>
$('#btnUpload').click(function () {
$.ajax({
url: "uploadvideo",
type:'POST',
data: $("#virtualtourfile:file"),
success: function (data) {
}
});
});
Controller
public ActionResult UploadVideo(HttpPostedFileBase file)
{
return Json("", JsonRequestBehavior.AllowGet);
}
There are a couple of options. If the client browser supports the HTML5 File API you could use it to upload a file asynchronously to the server. If you need to support legacy browsers that do not support this API you could use a file upload component such as Uploadify, Fine uploader, jquery form, ... The advantage of those plugins is that they will test the capabilities of the browser and if it supports the File API it will use it, otherwise it will fallback to older techniques such as hidden iframes or Flash movies.
I've used a few plugins and I found the Kendo UI upload plugin nice, here is a link how it works:
http://demos.kendoui.com/web/upload/async.html
and you can find the sample project for Asp.Net MVC 3 here:
http://www.kendoui.com/forums/ui/upload/upoad-with-mvc.aspx
[HttpPost]
public ActionResult Save(IEnumerable<HttpPostedFileBase> attachments)
{
// The Name of the Upload component is "attachments"
foreach (var file in attachments)
{
// Some browsers send file names with full path. This needs to be stripped.
var fileName = Path.GetFileName(file.FileName);
var physicalPath = Path.Combine(Server.MapPath("~/App_Data"), fileName);
file.SaveAs(physicalPath);
}
// Return an empty string to signify success
return Content("");
}

AJAX file upload in Play Framework 2.1 RC1 delivers an empty file

Scala/Play gurus out there.
I'm trying to upload a file using AJAX, in Play 2.1 (RC1). For the client part I'm using eldarion/bootstrap-ajax and everything seems to be fine, except that the uploaded file is empty.
The front-end snippet:
...
<form action="#routes.Campaigns.upload" method="post" class="form ajax replaceable" data-replace=".replaceable">
<input type="file" name="picture">
<p><input class="btn" type="submit"></p>
</form>
...
Note that I had to use the explicit <form> tag instead of the #form helper, due to the fact that the required css class (data-replace) contains a dash, and therefore can not be used as a Symbol. But anyway. The called action in the controller looks like this:
def upload = Action(parse.temporaryFile) {
request =>
Logger.info("Trying to upload a file")
val resultString = try {
val file = new File("/tmp/picture")
request.body.moveTo(file, true)
"file has been uploaded"
} catch {
case e: Exception => "an error has occurred while uploading the file"
}
val jsonResponse = Json.toJson(
Map("html" -> Json.toJson("<p>" + resultString + "</p>")
)
)
Ok(jsonResponse)
}
I'm aware that as my development goes forward the file name should be more intelligently set, but for the moment being, /tmp/picture is for me as good a name as any other one.
The JSON response gets generated (with the "file has been uploaded" message within), and is sent back to the browser as the payload of the 200 response. The JSON is received and correctly used to modify the page (in this case, merely removing the very uploading form).
But the file, although appearing in the right moment and in the right place, is always empty:
larsson:tmp bruno$ ls -l /tmp/picture
-rw-r--r-- 1 bruno staff 0 7 Jan 03:07 /tmp/picture
That's specially strange, in my opinion, because the uploading code which uses a traditional multipart/form-data form, with no AJAX whatsoever, and an Action with parse.multipartFormData as a parameter, instead of parse.temporaryFile, works finely.
Any help will be very appreciated. Thanks in advance.
I don't know bootstrap-ajax, anyway if it hasn't dedicated support for uploading files via AJAX (and I didn't find any info about that possibility in its readme file) it will NOT send files with AJAX.
Reason: In standard JavaScript uploading files with AJAX is not possible due the security limits and there are some techniques to workaround this, mainly using iFrames, however I can't see nothing similar in the code of bootstrap-ajax so probably you need to modify it or use other solution.
Solution: There are some AJAX file uploaders, which works good with HTML5 ie. jQuery File Upload, which offers ajax upload, multi-file uploads, drag file to the drop zone etc.
In general HTML5 supports file uploads better than earlier versions of HTML, so you can build uploader easily without need of using additional plugins, take a look to this topic. As you can see it delivers possibilities to validate some data BEFORE the upload and also offers progress bars.
I'm currently trying to implement something like this and I got a first version working. This is how I do it:
In my Controller I define a method for uploading files. In my case I use Action.async since I save stuff to my MongoDB with reactivemongo. I have removed that code so that it do not complicate this example.
What I do in this example is that I upload a csv file, save it to disk and then produce the first row back as a string to the user. In real life the method produces a list back so that user is able to choose which column represent what an so on.
I use mighty csv for csv parsing. GREAT LIB!
Application:
def upload = Action.async(parse.multipartFormData) {
implicit request =>
val result = uploadForm.bindFromRequest().fold(
errorForm => Future(BadRequest(views.html.index(errorForm))),
form => {
import java.io.File
request.body.file("csvFile").map {
csv =>
val path = current.configuration.getString("csv.job.new.file.path").getOrElse("")
val name = DateTime.now().getMillis + ".csv"
csv.ref.moveTo(new File(path + name))
val settings = CSVReaderSettings.Standard(linesToSkip = form.linesToSkip)
val rows: Iterator[Array[String]] = CSVReader(path + name)(settings)
val firstRow = rows.next()
val test = firstRow match {
case xs if xs.size == 0 || xs.size == 1 => xs.mkString
case xs if xs.size > 1 => xs.mkString(", ")
}
Future(Ok(test))
}.getOrElse(Future(BadRequest("ahadasda")))
}
)
result
}
routes:
POST /upload #controllers.Application.upload
I use # before the controllers because I use DI with guice for my service classes.
Since we will use javascript for uploading we need to define our jsRoutes:
jsRoutes:
def javascriptRoutes = Action {
implicit request =>
import routes.javascript._
Ok(
Routes.javascriptRouter("jsRoutes")(
Application.upload
)
).as("text/javascript")
}
Remember to import in your template where you want to use the routes:
<script type="text/javascript" src="#routes.Application.javascriptRoutes"></script>
<script src="#routes.Assets.at("javascripts/app.js")#Messages("js.version")" type="text/javascript" ></script>
In my view template I have a regular helper form. There is some css style stuff I do to
change the looks and feel of the upload button and file chooser. But the input fields
are there.
index.scala.html:
<div class="csvContainer">
#helper.form(action = routes.Application.upload, 'enctype -> "multipart/form-data", 'id -> "csvUpload") {
#Messages("upload.row.skip")
#inputText(uploadForm("linesToSkip"), 'class -> "hidden")
<div style="position:relative;">
<div id="csvFile" style="position:absolute;">
#Messages("upload.choose")
</div>
<input id="uploadFile" type="file" name="csvFile" style="opacity:0; z-index:1;" onchange="document.getElementById('csvFile').innerHTML = this.value;" />
</div>
<p>
<input type="submit" value="#Messages("upload.submit")">
</p>
}
</div>
In app.js is where the ajax magic happens, remember I have not implemented any validation or cool html5 stuff yet as the progressbar and other handlers, described in besiors link.
I use regular JQuery.
app.js:
$('#uploadFile').change(function(){
var name = $(this).val().split("\\");
console.log(name[2]);
$('#csvFile').text(name[2]);
});
$('#csvFile').click(function(){
$('#uploadFile').click();
});
$("#csvUpload").submit(function(e) {
e.preventDefault();
var formData = new FormData();
formData.append('csvFile', $( '#uploadFile' )[0].files[0]);
formData.append('linesToSkip', $( "#linesToSkip").val());
jsRoutes.controllers.Application.upload().ajax({
data: formData,
processData: false,
contentType: false,
cache: false,
type: 'POST',
success: function(data){
alert(data);
}
});
});
I have removed a lot of code to simplify this example and I hope that I have not forgotten anything. Hope this helps!

jquery - load all text files and display them

I have been using jQuery pretty long time, but I never learned AJAX, so here I come..
we have this code :
$('body').load('hello.txt');
Simple enough, now let's say I have multiple text files (I don't know their names) I want to load,
Can I do that ?
Maybe I need to loop all the text files and load them somehow ?
Thanks in Advance
Assuming you have text files in the server in a specific location you can do this:
HTML markup:
<div id="fileList">
here list of files will be loaded so that user can select which one to load
<div>
<div id="file-content">
content of selected file will be loaded here
<div>
JQuery part :
$.ajax({
url : "FileServer/GetFileNames", // this is just a url that is responsible to return files list
success : function(data){
//here a JSON data including filenames expected
$.each(data,function(i,item){
var $fileHolder = $("<div></div>");
$fileHolder.attr("filename",item.filename).click(function(){
$("#file-content").load($(this).attr("filename"));
}).html(item.filename).appendTo("#fileList");
});
}
});
JSON Structure expected
[
{
filename : "text1.txt"
},
{
filename : "text2.txt"
},
{
filename : "text3.txt"
}
]
implementing file listing in the server side is up to you.
Javascript does not have access to the local file system for obvious
security reasons. This is not possible.
Unless you are trying to loop through files on your server, in which
case you wouldn't want to use jQuery anyway but something like ASP.NET
or PHP or whatever framework you are using.
Foreach file in directory jQuery
UPDATE
Try this out
var files;
$.ajax({
url: "http://homepage/folder",
success: function (txt) {
files = txt.split('<A href="');
}
});
var fList = new Array();
$(files).each(function () {
if (this.indexOf('.txt') > -1) {
fList.push(this);
}
});
for (i = 0; i < fList.length; i++) {
fList[i] = fList[i].split('">')[0];
fList[i] = fList[i].replace('"');
}
for (i = 0; i < fList.length; i++) {
$('#idLoadHere').load(fList[i]);
}
Run FTP list command (there are various ways to do so, Web-Sockets is one..)
A simpler, more common ans secure-solution is a server-side listing of the files, and "cooking" the HTML (meaning- embedding the file-listing within it),
*you can use raw HTML or put it in var statement to be used by JavaScript (for example).
see following answer:
https://stackoverflow.com/a/30949072/257319

JQuery UI Autocomplete not working in ASP.NET MVC

I am trying to implement an autocomplete in ASP.NET MVC 3, following this post
but I really can't get it to work. I have looked at a series of other posts and blogs, but no success so far.
I have a DB table which contains regions (in Japanese), like so:
1 ハイチ; 2 ドミニカ共和国; 3 南アフリカ
basically a [key, Name] pair.
In my repository I am doing the following call:
public IQueryable<Region> GetAllRegions()
{
return db.Regions;
}
Getting the raw data, which I pass to the controller, pair down the data according to input and the concatenate to a string like so:
public class RegionsController : Controller
{
Region_Repository rr = new Region_Repository();
public string FindRegions(string q)
{
List<string> regions = rr.GetAllRegions().Select(r => r.Name).Where(s => s.StartsWith(q)).ToList();
return string.Join("\n", regions);
}
}
The controller when accessed via server/Regions/FindRegions/?q=ハ return a page with the single entry "ハイチ" as expected.
On the page I have a textbox with id "#NewRegion" and the script
<script type="text/javascript">
$(document).ready(function () {
$("#NewRegion").autocomplete('#Url.Action("FindRegions", "Regions")');
})
</script>
which I placed underneath the textbox.
I have references to JQuery and JQueryUI from Google API which work since I am executing other JQuery and using the JQueryUI datepicker successfully on this page.
I tried placing the script in a separate file and hardcoding the url as '/Regions/FindRegions', but no change. I had a look in firebug (second day only, so not very proficient at using it yet) and the script doesn't seem to get executed.
The controller is called fine when accessed via URL, but anything I enter into the textbox does not get passed to the controller...
Can anybody see anything wrong with this?
According to the dark depths of the jQuery UI documentation, the parameter used when passing the autocomplete string to the server is called term, not q.
Update: And also, initialize with autocomplete({source: "#(...url...)"}).
Update 2: And also, return the type of data the Autocomplete widget expects, a JSON array of strings or a JSON array of objects.
I got it working in the following way. I changed the controller to return Json data like this:
public ActionResult FindRegions(string term)
{
var regionNames = rr.GetAllRegions().Select(r => r.Name).Where(s => s.Contains(term)).ToList();
return Json(regionNames, JsonRequestBehavior.AllowGet);
}
note: the AllowGet is there so I could check the result via url, so for debug only.
And the autocomplete script became:
$(function () {
$('#NewRegion').autocomplete({ source: '/Regions/FindRegions' } );
});
Now it works, but I am still not sure why.

use html5 multiple attribute to trigger multiple single uploads

Sorry for the confusing title.
I have a form-- form1 that has one file input ( with multiple attribute set so that user can select mutiple files). The form doesn't get submitted.
I have another form -- form2 that has a single file input . no mutiple attribute.
Now via javascript i would like to fetch each files from the fileinput from the previous form and then assign the file to the form2's input field and then do an ajax submit.
Once the ajax submit is complete I would like to do the same to 2nd file and then 3rd file and so on.
I don't want to use flash or java applet.
I am fully aware that IE doesn't support multiple attribute
opera can use invalid min max attribute to do the same.
My basic question would be how to fetch the files from the form1 input field and then assisgn it to form2's field ..
Is there a solution for this ?
or is my approach itself incorrect ?
What I want to achieve on UI side ?
The file gets uploaded and server does some processing and returns some data.
so what I want is user can select 10 files but as soon as 1st file is uploaded the output is received.
First : My idea is wrong.
We cannot assign a value via javascript to the input with type = file .
I thought of another idea using the XMLHttpRequest.
here is my code :
<form id="new_picture_form" method="post" enctype="multipart/form-data" action="some url" accept-charset="UTF-8">
<input id="original_input" class="file_hidden" type="file" onchange="handleFiles(this.files);" name="picture[image][]" multiple="multiple">
</form>
<form id="fileinfo" method="post" enctype="multipart/form-data" action="some url">
</form>
<script type="text/javascript">
function handleFiles(files)
{
for (var i = 0; i < files.length; i++) {
FileUpload(files[i])
}
}
function FileUpload(file) {
var data = new FormData(document.getElementById("fileinfo"));
var xhr = new XMLHttpRequest();
this.xhr = xhr;
data.append('some field',"that you want to pass as param ")
data.append('type',"picture")
data.append("picture[image]", file);
xhr.open("POST", "URL",true);
xhr.send(data);
xhr.onreadystatechange = function() {
if (xhr.readyState == 4) {
eval(xhr.responseText) // basically the result from server contains some script
}
}
}
</script>

Resources