Uploading file AND text to server using Ajax in asp.net mvc - asp.net

Greeting.
I want to upload an image together with a description to the server by using ajax.
The fileupload works fine, although I can't figure out how to also extract the text entered.
Basically the form looks like this:
<form id="uploader">
<input id="fileInput" type="file" multiple>
<input type="text" id="fileText" name="fileText" value=" " />
<input type="submit" value="Upload file" />
</form>
The script for the upload to the server looks like this:
document.getElementById('uploader').onsubmit = function () {
var formdata = new FormData(); //FormData object
var fileInput = document.getElementById('fileInput');
//Iterating through each files selected in fileInput
for (i = 0; i < fileInput.files.length; i++) {
//Appending each file to FormData object
formdata.append(fileInput.files[i].name, fileInput.files[i]);
}
//Creating an XMLHttpRequest and sending
var xhr = new XMLHttpRequest();
xhr.open('POST', '/Controller/Action');
xhr.send(formdata);
xhr.onreadystatechange = function () {
if (xhr.readyState == 4 && xhr.status == 200) {
alert(xhr.responseText);
}
}
return false;
}
And on the serverside, in the controller for the related action:
public JsonResult Upload(){
for (int i = 0; i < 9; i++){
HttpPostedFileBase file = Request.Files[i]; //Uploaded files
//Do stuff with uploaded files
}
}
What I've tried:
Changed the Upload method to take in a string parameter named fileText.
Changed the script to also appent the text to the formdata with these two lines inside the for-loop:
var fileText = document.getElementById('fileText');
formdata.append(fileText.value, fileText.value)
I'm probably missing something in both of my trials but I can't seem to figure out what. Please help!
Regards,
Chris

Changed the Upload method to take in a string parameter named fileText.
Correct.
Changed the script to also appent the text to the formdata with these two lines inside the for-loop:
Incorrect. You shouldn't be doing this inside the loop because you have only one input field for the text, so you can send only 1 value. So move this code outside of the loop. Also you should specify the correct name when appending to the FormData which must match your controller action parameter name:
var fileText = document.getElementById('fileText');
formdata.append('fileText', fileText.value);

Related

Why does Servlet think my empty HTML file element has a file in it?

I am using a Java Servlet to handle an html form and it includes a file input element:
<input type="file" id="fileUpload" name="file" multiple />
I used the example code in this excellent answer to process multiple files at once. The code I'm using is:
List<Part> fileParts = req.getParts().stream().filter(part -> "file".equals(part.getName())).collect(Collectors.toList()); // Retrieves <input type="file" name="file" multiple="true">
for (Part filePart : fileParts) {
String fileName = filePart.getSubmittedFileName();
InputStream fileContent = filePart.getInputStream();
// Do stuff here
}
This code works great. My problem is, when I don't attach anything, my code still thinks fileParts has a Part object in it. Doing some debugging, the Part object does seem to be there, but of course there is no InputStream or SubmittedFileName to get, since I didn't upload any files. Why is this? I am new to lambda functions and collections, but it seems like this "fileParts" Collection should be empty when I don't select any files for upload.
This is just how HTML works.
The same is true for non-file inputs. When an empty input is submitted, you get an empty value, not null. The difference is significant. A value of null represents absence of the input field. This is particularly useful when the form has multiple submit buttons and you'd like to distinguish the button pressed.
Given a <input type="text" name="foo">,
String foo = request.getParameter("foo");
if (foo == null) {
// foo was not submitted at all.
} else if (foo.isEmpty()) {
// foo is submitted without value.
} else {
// foo is submitted with a value.
}
And a <input type="file" name="bar">,
Part bar = request.getPart("bar");
if (bar == null) {
// bar was not submitted at all.
} else if (bar.getSize() == 0) {
// bar is submitted without value.
} else {
// bar is submitted with a value.
}

How to call async function inside Web form/ MVC Razor Asp.net? [duplicate]

Is it possible to await on tasks in Razor .cshtml views?
By default it complains that it can only be used in methods marked with async so I'm wondering if maybe there is a hidden switch somewhere that enables it?
In ASP.NET Core 2.1, you can use await in Razor views.
See https://learn.microsoft.com/en-us/aspnet/core/mvc/views/partial?view=aspnetcore-2.1
Example:
#await Html.PartialAsync("../Account/_LoginPartial.cshtml")
I've wanted something like this for a long time - a lot of the pages we write could be thrown together by a Jr Dev if they didn't have to write a bunch of queries; and, it's the same basic query boilerplate every time anyway - why should they have to write them for each Controller, when the majority of their work is to get content up? I use C# so I don't have to deal with memory management, why should an HTML coder have to deal with query details?
There is a trick you can use to sort of implicitly load data async into the View. First, you define a class that expresses what data you want. Then, at the top of each View, instantiate that class. Back in the Controller, you can lookup the View you know you're going to use, open it, then compile that class. You can then use it to go get the data the View will need, async, in the Controller the way MVC enforces. Finally, pass it off with a ViewModel to the View as MVC prescribes, and, through some trickery - you have a View that declares what data it's going to use.
Here's a StoryController. Jr Devs write stories as simple .cshtml files without having to know what a Controller, database or LINQ is:
public class StoryController : BaseController
{
[OutputCache(Duration=CacheDuration.Days1)]
// /story/(id)
public async Task<ActionResult> Id(string id = null)
{
string storyFilename = id;
// Get the View - story file
if (storyFilename == null || storyFilename.Contains('.'))
return Redirect("/"); // Disallow ../ for example
string path = App.O.AppRoot + App.HomeViews + #"story\" + storyFilename + ".cshtml";
if (!System.IO.File.Exists(path))
return Redirect("/");
return View(storyFilename);
All this does for now is go get the View file based on the URL, allowing something like WebForms (except inside MVC and using Razor). But we want to show some data - in our case, people and projects that accumulate in the database - with some standard ViewModels and Partials. Let's define how and compile that out. (Note that ConservX happens to be the core Project namespace in my case.)
public async Task<ActionResult> Id(string id = null)
{
string storyFilename = id;
// 1) Get the View - story file
if (storyFilename == null || storyFilename.Contains('.'))
return Redirect("/"); // Disallow ../ for example
string path = App.O.AppRoot + App.HomeViews + #"story\" + storyFilename + ".cshtml";
if (!System.IO.File.Exists(path))
return Redirect("/");
// 2) It exists - begin parsing it for StoryDataIds
var lines = await FileHelper.ReadLinesUntilAsync(path, line => line.Contains("#section"));
// 3) Is there a line that says "new StoryDataIds"?
int i = 0;
int l = lines.Count;
for (; i < l && !lines[i].Contains("var dataIds = new StoryDataIds"); i++)
{}
if (i == l) // No StoryDataIds defined, just pass an empty StoryViewModel
return View(storyFilename, new StoryViewModel());
// https://stackoverflow.com/questions/1361965/compile-simple-string
// https://msdn.microsoft.com/en-us/library/system.codedom.codecompileunit.aspx
// https://msdn.microsoft.com/en-us/library/system.codedom.compiler.codedomprovider(v=vs.110).aspx
string className = "__StoryData_" + storyFilename;
string code = String.Join(" ",
(new[] {
"using ConservX.Areas.Home.ViewModels.Storying;",
"public class " + className + " { public static StoryDataIds Get() {"
}).Concat(
lines.Skip(i).TakeWhile(line => !line.Contains("};"))
).Concat(
new[] { "}; return dataIds; } }" }
));
var refs = AppDomain.CurrentDomain.GetAssemblies();
var refFiles = refs.Where(a => !a.IsDynamic).Select(a => a.Location).ToArray();
var cSharp = (new Microsoft.CSharp.CSharpCodeProvider()).CreateCompiler();
var compileParams = new System.CodeDom.Compiler.CompilerParameters(refFiles);
compileParams.GenerateInMemory = true;
compileParams.GenerateExecutable = false;
var compilerResult = cSharp.CompileAssemblyFromSource(compileParams, code);
var asm = compilerResult.CompiledAssembly;
var tempType = asm.GetType(className);
var ids = (StoryDataIds)tempType.GetMethod("Get").Invoke(null, null);
using (var db... // Fetch the relevant data here
var vm = new StoryViewModel();
return View(storyFilename, vm);
}
That's the majority of the work. Now Jr Devs can just declare the data they need like so:
#using ConservX.Areas.Home.ViewModels.Storying
#model StoryViewModel
#{
var dataIds = new StoryDataIds
{
ProjectIds = new[] { 4 }
};
string title = "Story Title";
ViewBag.Title = title;
Layout = "~/Areas/Home/Views/Shared/_Main.cshtml";
}
#section css {
...
I landed on this question because I am a newbie to Razor and I wanted to display a simple "loading..." screen while my Controller Code was calculating data.
So I found this link: https://www.codeproject.com/Articles/424745/MVC-Razor-In-Progress-Icon which was helpful, but because I was a total novice at Razor, I was unable to make this work.
What finally worked for me was the following.
1) Add the "loading" div as suggested in the code project to my .cshtml file:
<div id="divLoading" style="margin: 0px; padding: 0px; position: fixed; right: 0px;
top: 0px; width: 100%; height: 100%; background-color: #666666; z-index: 30001;
opacity: .8; filter: alpha(opacity=70);display:none">
<p style="position: absolute; top: 30%; left: 45%; color: White;">
Loading, please wait...<img src="../../Content/Images/ajax-loading.gif">
</p>
</div>
2) Modify my Razor form from
<input type="submit" value="Go"/>
to
<input type="button" value="Go" onclick="JavascriptFunction()" />
3) Create the JavascriptFunction() in my .cshtml page:
<script type="text/javascript" language="javascript">
function JavascriptFunction() {
$("#divLoading").show();
$('form').submit();
}
</script>
If I understand all of the above correctly, what this does is execute the function JavascriptFunction when I press the Go button.
The JavascriptFunction does 2 things:
1) Change the view of the page by showing the previously hidden (display:none) divLoading div.
2) Submit all the forms on this page (I only have one, so it submits the form the same as if I had they type submit on the button)
After the Controller launched by the form submit is done, it loads a new view on a new page, and the initial page (and the "loading" div) is gone. Mission accomplished.
You can await calls in razor pages? I have a Blazor app and most of my methods are async:
Razor page:
<MatFAB Icon="#MatIconNames.Autorenew" Style="transform:scale(0.8); background:#333;"
OnClick="#(async () => await OnInitializedAsync())"></MatFAB>
This is a MatBlazor FloatingActionButton which calls the life time cycle event OnInitializedAsync()
C# Code:
protected override async Task OnInitializedAsync()
{
// Do something like get data when the form loads
}
No, that's not possible and you shouldn't need to do it anyway. Razor views should contain markup and at most some helper call. async/await belongs to your backend logic.
If you really need it, you can do this, it will be ugly, but it will work.
In View
#{
var foo = ViewBag.foo;
var bar = ViewBag.bar;
}
In Controller
public async Task<ActionResult> Index()
{
ViewBag.foo = await _some.getFoo();
ViewBag.bar = await _some.getBar();
return View("Index");
}
Following on MaxP's answer, it's easy to return a value from that code, despite Knagis comment:
#{
int x = DoAsyncStuffWrapper().Result;
}
#functions {
async Task<int>DoAsyncStuffWrapper()
{
await DoAsyncStuff();
}
}
I know this is an older thread, but I'll add my input just in case someone else finds it useful. I ran into this problem working with the new MongoDB driver in ASP.Net MVC - the new driver (for now), only implements async methods and returns async cursors, which can't be used in a foreach because asynccursor doesn't implement IEnumerable. The sample code typically looks like:
while(await cursor.movenextasync)
var batch=cursor.current
foreach(var item in batch)
--do stuff here--
But, this doesn't work in razor, because views are inherently not async, and await doesn't cut it.
I got it to work by changing the first line to:
while(cursor.MoveNextAsync().Result)
which returns true until the cursor hits the last entry.
Hope that helps!

Error in displaying image in HTML file

In my MVC2 application I use CKEditor where I allow the user to create a PDF document.
First the CKEditor content will get converted into HTML file and later as PDF document.
I have provided a button called Arrow on click of it image of an arrow should get inserted. In editor image gets displayed successfully but in HTML and PDF file image is not getting displayed inplace of it alt content gets displayed.
Code for an arrow button:
<input type="button" class="green_button" id="arrow" name="Arrow" value="Arrow" style="width: 110px; height: 30px; background-color: #FFFFFF;" onclick="return arrow_onclick()" />
function arrow_onclick() {
var editor = CKEDITOR.instances.message;
editor.insertHtml(' <input type="image" alt="arrow" src="../../PDFimages/arrow-left-up.jpg" style="height:100px; width:100px" />');
}
Controller code:
public ActionResult CreateFile(FormCollection data)
{
var filename = data["filename"];
var htmlContent = data["content"];
string sFilePath = Server.MapPath(_createdPDF + filename + ".html");
htmlContent = htmlContent.Trim();
if (!System.IO.File.Exists(sFilePath))
{
using (FileStream fs = new FileStream(sFilePath, FileMode.Create))
{
using (StreamWriter w = new StreamWriter(fs, Encoding.UTF8))
{
w.Write(htmlContent);
}
}
string filename1 = Path.GetFileNameWithoutExtension(sFilePath);
string name = Server.MapPath(_createdPDF + filename1 + ".pdf");
HTML2PDF.SetModulePath(#"C:\Documents and Settings\shubham\My Documents\visdatemplatemanger\visdatemplatemanger\bin");
using (PDFDoc doc = new PDFDoc())
{
if (HTML2PDF.Convert(doc, sFilePath))
doc.Save(name, pdftron.SDF.SDFDoc.SaveOptions.e_linearized);
}
System.IO.File.Delete(sFilePath);
UploadURL(name);
}
return View();
}
Before submit your form, you should set textarea value from CkEditor and make your action that accepts the post has this annotation applied [HttpPost, ValidateInput(false)]
$("#content").val(CKEDITOR.instances.message.getData());
And check your src value. Maybe should using:
src="#(Url.Content( "~/PDFimages/arrow-left-up.jpg" ))"
if PDFimages folder included in Project/Content

Qt Webkit - Browser Interaction issue

I'm developing a Qt program that contains an OpenStreetMap application as a HTML page and this page is able to access a database -via submitting an ajax form that contains the start and end dates of queries- in order to retrieve and visualize queries on the map. I would like to move this querying process to Qt from the HTML/Javascript part. So far I managed to interact with the browser via Qt but I still have a problem that is below:
1) The fetch queries button of Qt is clicked and an alert box is supposed to pop up saying that Ajax POST is failed -the database is not on my current laptop and I should be getting the error when I click either the HTML Browser window's fetch queries button or the Qt's fetch button-
2) But also, whenever I click the Fetch queries button of the HTML Browser, it displays the POST warning but also displays extra POST warning alert boxes depending on how many times I have clicked the Qt's Fetch queries button. -for example if I have clicked the Qt's fetch queries button 5 times in a row and then clicked the HTML window's fetch button once, I get 6 POST failed messages in a row-
The HTML code is like the following:
<form id="ajaxForm" action="index.php" method="post">
Start <input type="text" name = "date1" id = "datepicker" value = "2011-07-13" style = "width:70px">
<input type="text" name = "time1" id = "timepicker1" value = "00:00" style = "width:40px">
End <input type="text" name = "date2" id = "datepicker2" value = "2011-07-13" style = "width:70px">
<input type="text" name = "time2" id = "timepicker2" value = "00:01" style = "width:40px">
The post method of AJAX form is this:
<script type="text/javascript">
$(document).ready(function(){
// ajaxForm submit
$('#ajaxForm').submit(function() {
$.ajax({
type: 'POST',
url: 'heatQuery.php',
data: $(this).serialize(),
dataType: 'json',
success: function(response)
{
// update the points for heatmap layer
updateHeatMap(response);
},
error: function(errorMsg)
{
alert('Error in Ajax POST');
}
});
return false;
});
});
</script>
And finally, the Qt code that calls the function is this:
void MainWindow::onButtonClicked() // clicking the button in order to POST
{
//the QString a is the same ajax post function as declared above
QString a = "$(document).ready(function(){$('#ajaxForm').submit(function() {$.ajax({type: 'POST',url: 'heatQuery.php',data: $(this).serialize(),dataType: 'json',success: function(response){updateHeatMap(response);},error: function(errorMsg){alert('Error in Ajax POST');}});return false;});});";
this->view->page()->mainFrame()->evaluateJavaScript(a);
}
Any ideas on what is wrong here? Thanks.
I think I have got the problem. XMLHttpRequest loads your local file successfully, but it returns 0 in request.status, thats why error() gets fired from your jQuery code.
I ran following example code in QWebView..
var request = new XMLHttpRequest();
request.open('POST', 'file:///C:/hello.txt', true);
request.onreadystatechange = function(e) {
if(request.readyState == 4)
{
// 'responseText' will not be empty if file present..
alert("response text: " + request.responseText);
alert("status: " + request.status);
}
}
request.send();
Hope this helps..

Access variablea of javascript in .aspx.vb file

I want to access variables defined in Javascript in.aspx file to .aspx.vb file
How can i access variables in .aspx.vb file?
<script type="text/javascript" language="javascript">
var c=0;
var m=0;
var h=0;
var t;
var timer_is_on=0;
function startTMR()
{
document.getElementById('TimeLable').value=h+":"+m+":"+c;
c=c+1;
if(c==60)
{
c=0;
m=m+1;
if(m==60)
{
m=0;
h=h+1;
}
}
t=setTimeout("startTMR()",1000);
}
function doTimer()
{
if (!timer_is_on)
{
timer_is_on=1;
startTMR();
}
}
This is simple javascript I'm using in my .aspx page
now i want to access the variable h m and c in .aspx.vb page how to do that?
You'll need to save that javascript variable into a hidden input, which will post with your form when you do a postback. You'll be able to access the value via:
string value = Request.Form["hiddenName"];
Assuming you declare your hidden input like this:
<input type="hidden" id="hiddenValue" name="hiddenName" />
You can set this value like this with native JavaScript:
document.getElementById("hiddenValue").value = "12";
or with jQuery like this:
$("#hiddenValue").val("12");
If you'd like to make sure this hidden input is automatically saved to the JavaScript variable x before you post back, you could do the following with jQuery
$("#form1").submit(function () {
$("#hiddenValue").val(x);
});
Or this with native JavaScript:
document.getElementById("form1").addEventListener("submit", function () {
document.getElementById("hiddenValue").value = x;
});
If you're not using jQuery, and you opt for this native option, just make sure you put the script at the bottom of the body section; do not put this script in the head, since the dom is not formed yet, and you will get a null error.
And obviously this all assumes your form element looks like this:
<form id="form1" runat="server">
if yours has a different id, then adjust accordingly.

Resources