I'm using Uploadify to upload some images with ASP.NET.
I use Response.WriteFile() in ASP.NET to return the result of the upload back to JavaScript.
As specified in the documentation I'm using onAllComplete event to check for response string from ASP.NET.
The problem is it that the alert(response); is always undefined in JavaScript.
JavaScript code as below:
$(document).ready(function() {
var auth = "<% = Request.Cookies[FormsAuthentication.FormsCookieName]==null ? string.Empty : Request.Cookies[FormsAuthentication.FormsCookieName].Value %>";
$('#btnUpdateProfileImg').uploadify({
'uploader': '../script/uploadify/uploadify.swf',
'script': '../uploadprofimg.aspx',
'cancelImg': '../script/uploadify/cancel.png',
'folder': '../script/uploadify',
'scriptData': { 'id': $(this).attr("id"), 'token': auth },
'onAllComplete': function(event, queueID, fileObj, response, data) {
alert(response);
}
});
});
ASP.NET code a below;
protected void Page_Load(object sender, EventArgs e)
{
try
{
string token = Request.Form["token"].ToString();
FormsAuthenticationTicket ticket = FormsAuthentication.Decrypt(token);
if (ticket != null)
{
var identity = new FormsIdentity(ticket);
if (identity.IsAuthenticated)
{
HttpPostedFile hpFile = Request.Files["ProfileImage"];
string appPath = HttpContext.Current.Request.ApplicationPath;
string fullPath = HttpContext.Current.Request.MapPath(appPath) + #"\avatar\";
hpFile.SaveAs(Server.MapPath("~/" + uniqName));
Response.ContentType = "text/plain";
Response.Write("test");
}
}
}
catch (Exception ex)
{
Response.Write("test");
}
}
Reason for the FormsAuthenticationTicket object is to pass the authentication cookie though when using the Uploadify with Firefox.
I have seen many examples where Response.Write returns a value back to the onAllComplete event. But all I get is undefined.
I have also tried to use Context.Response.Write, this.Response.Write, HttpContext.Current.Response.Write. They all return undefined.
Any help appreciated.
Thanks
It seems that the onAllComplete event never fires. This is possibly because I'm automatically uploading single files rather than multiple files.
I find that the onComplete event fires and I can use that instead.
Related
I'm creating an ASP.NET MVC application which uses "SqlDependecy" and "SignalR" technologies to maintain real-time communication with the server based on database changes. It simply inspect a field value changes in specific database record and then display it on the browser.
The attempt works perfectly fine. But when I monitor the network requests through the browsers "Network" performance, the request count increases by 1 in every refresh of the page.
As in the image.
Initial page load only make one request.
First refresh after the initial load and then db change will lead to make 2 requests.
Second refresh after the initial load and then db change will lead to make 3 requests.
so on...
The js code I tried is given below.
It seams as an problem to me. If this is a real problem, Any advice on this will be highly appreciated. Thank you very much.
<script type="text/javascript">
$(function () {
var jHub = $.connection.journeyHub;
$.connection.hub.start();
jHub.client.ListenChange = function () {
getData();
}
jHub.client.ListenChange();
});
function getData() {
$.ajax({
url: 'GetValue',
type: 'GET',
dataType: 'json',
success: function (data) {
if (data == "pending") {
$("#box").css({ "background-color": "orange" });
}
else if (data == "deny") {
$("#box").css({ "background-color": "red" });
}
else if (data == "success") {
$("#box").css({ "background-color": "green" });
}
}
});
}
</script>
<div id="box" style="width:100px; height:100px; background-color: gray;"></div>
[Edit v1]
Here is my Controller where the event handler is located.
public class TravelController : Controller
{
SqlConnection link = new SqlConnection(ConfigurationManager.ConnectionStrings["linkTraveller"].ConnectionString);
// GET: Travel
public ActionResult Listen()
{
return View();
}
public ActionResult GetValue()
{
using (IDbConnection conn = link)
{
string query = #"SELECT [Status] FROM [dbo].[Journey] WHERE [Id]=1";
SqlCommand command = new SqlCommand(query, link);
SqlDependency sqlDep = new SqlDependency(command);
sqlDep.OnChange += new OnChangeEventHandler((sender, e) => sqlDep_OnChange(sender, e));
conn.Open();
string status = command.ExecuteScalar().ToString();
return Json(status, JsonRequestBehavior.AllowGet);
}
}
private void sqlDep_OnChange(object sender, SqlNotificationEventArgs e)
{
JourneyHub.Start();
}
}
Here is the Hub
public class JourneyHub : Hub
{
public static void Start()
{
var context = GlobalHost.ConnectionManager.GetHubContext<JourneyHub>();
context.Clients.All.ListenChange();
}
}
Off the top of my head, I would say you are not decrementing your trigger handlers, sql dependency triggers only fire once and then they are gone, you have to remember the remove the event handler for it or they just keep adding but, but I will know for sure if you can post your sql dependency trigger code.
Here is a sample from something I did many years ago, but the idea is still the same.
try
{
using (
var connection =
new SqlConnection(ConfigurationManager.ConnectionStrings["DefaultConnection"].ConnectionString))
{
connection.Open();
using (SqlCommand command = new SqlCommand(#"SELECT [Id]
,[FName]
,[LName]
,[DOB]
,[Notes]
,[PendingReview]
FROM [dbo].[Users]",
connection))
{
// Make sure the command object does not already have
// a notification object associated with it.
command.Notification = null;
SqlDependency dependency = new SqlDependency(command);
dependency.OnChange += new OnChangeEventHandler(dependency_OnChange);
if (connection.State == ConnectionState.Closed)
connection.Open();
command.ExecuteReader();
}
}
}
catch (Exception e)
{
throw;
}
}
private void dependency_OnChange(object sender, SqlNotificationEventArgs e)
{
SqlDependency dependency = sender as SqlDependency;
if (dependency != null) dependency.OnChange -= dependency_OnChange;
//Recall your SQLDependency setup method here.
SetupDependency();
JobHub.Show();
}
If i go directly to the URL from my Program which generates a PDF i get a direct Download.
public void Download(byte[] file)
{
try
{
MemoryStream mstream = new MemoryStream(file);
long dataLengthToRead = mstream.Length;
Response.Clear();
Response.ClearContent();
Response.ClearHeaders();
Response.BufferOutput = true;
Response.ContentType = "Application/pdf"; /// if it is text or xml
Response.AddHeader("Content-Disposition", "attachment; filename=" + "Test.pdf");
Response.AddHeader("Content-Length", dataLengthToRead.ToString());
mstream.WriteTo(Response.OutputStream);
Response.Flush();
Response.Close();
HttpContext.Current.Response.Flush(); // Sends all currently buffered output to the client.
HttpContext.Current.Response.SuppressContent = true; // Gets or sets a value indicating whether to send HTTP content to the client.
HttpContext.Current.ApplicationInstance.CompleteRequest(); // Causes ASP.NET to bypass all events and filtering in the HTTP pipeline chain of execution and directly execute the EndRequest event.
}
catch (Exception e)
{
}
}
But if i do a POST with my JSON to the URL this Method doesnt give me a direct-Download.
The POST with the JSON-String is successfull. But the download doesn't start after the Ajax-Call. My Program is a simple ASPX-Website which load all Data and functions with the Page_Load-Event.
One way to go about this is to hit the ajax POST and on success of the call return the file download link and then redirect the browser to that link to download the file like so:
$.ajax({
url: "THE_POST_URL",
method: 'POST',
success: function (response) { //return the download link
window.location.href = response;
},
error: function (e) {
},
});
My problem is simple and complex same time:
Im tryin to upload files using jQuery fileUpload library with spring mvc controller as server side, but my files are being uploaded by one request each. What i want is posting them ALL in ONE request.
I have tried singleFileUploads: false option but its not working, if i pass 4 files to upload, the method responsible for handling the post is called 4 times.
Im using this form to post files:
<div class="upload-file-div">
<b>Choose csv files to load</b> <input id="csvUpload" type="file"
name="files[] "data-url="adminpanel/uploadCsv" multiple />
</div>
<div id="dropzoneCsv">Or drop files here</div>
<div id="progressCsv">
<div class="bar" style="width: 0%;"></div>
</div>
Jquery method to upload files:
$('#csvUpload').fileupload(
{
singleFileUploads: false,
dataType : 'json',
done : function(e, data) {
$("tr:has(td)").remove();
$.each(data.result, function(index, file) {
$("#uploaded-csv").append(
$('<tr/>').append(
$('<td/>').text(file.fileName))
.append(
$('<td/>').text(
file.fileSize))
.append(
$('<td/>').text(
file.fileType))
.append(
$('<td/>').text(
file.existsOnServer))
.append($('<td/>')));
});
},
progressall : function(e, data) {
var progress = parseInt(data.loaded / data.total * 100,
10);
$('#progressCsv .bar').css('width', progress + '%');
},
dropZone : $('#dropzoneCsv')
});
And handler method :
#RequestMapping(value = "/adminpanel/uploadCsv", method = RequestMethod.POST)
public #ResponseBody
List<FileMeta> uploadCsv(MultipartHttpServletRequest request, HttpServletResponse response) {
// 1. build an iterator
Iterator<String> itr = request.getFileNames();
MultipartFile mpf = null;
List<FileMeta> csvFiles = new ArrayList<FileMeta>();
// 2. get each file
while (itr.hasNext()) {
// 2.1 get next MultipartFile
mpf = request.getFile(itr.next());
System.out.println(mpf.getOriginalFilename() + " uploaded! ");
// 2.3 create new fileMeta
FileMeta fileMeta = new FileMeta();
fileMeta.setFileName(mpf.getOriginalFilename());
fileMeta.setFileSize(mpf.getSize() / 1024 + " Kb");
fileMeta.setFileType(mpf.getContentType());
try {
File dir = new File(Thread.currentThread().getContextClassLoader()
.getResource("").getPath()+"CSV");
if(!dir.exists()) dir.mkdirs();
File newCSV = new File(dir+"\\"+ mpf.getOriginalFilename());
if(!newCSV.exists())
{
mpf.transferTo(newCSV);
fileMeta.setExistsOnServer(false);
}
else fileMeta.setExistsOnServer(true);
} catch (IllegalStateException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
} catch (IOException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
}
// 2.4 add to files
csvFiles.add(fileMeta);
}
return csvFiles;
}
I would really need an assistance here :( Files should be loaded in one request, thats why im doing the iterator, but its just not working.
ps. Sorry for my terrible english :(
You may want to try Programmatic file upload instead. The send method will ensure only one request is issued.
Basically keep a filelist variable, update it everytime fileuploadadd callback happens, then use this filelist for the send method.
For example:
$document.ready(function(){
var filelist = [];
$('#form').fileupload({
... // your fileupload options
}).on("fileuploadadd", function(e, data){
for(var i = 0; i < data.files.length; i++){
filelist.push(data.files[i])
}
})
$('#button').click(function(){
$('#form').fileupload('send', {files:filelist});
})
})
It is inspired by this question.
The reason I found it useful is even if you set singleFileUploads to false, if you do multiple individual selections, they will still be sent with individual requests each, as the author said himself in this GitHub issue
I am seeing odd behavior with the code here.
Client-side (Javascript):
<input type="text" id="userid" placeholder="UserID" /><br />
<input type="button" id="ping" value="Ping" />
<script>
var es = new EventSource('/home/message');
es.onmessage = function (e) {
console.log(e.data);
};
es.onerror = function () {
console.log(arguments);
};
$(function () {
$('#ping').on('click', function () {
$.post('/home/ping', {
UserID: parseInt($('#userid').val()) || 0
});
});
});
</script>
Server-side (C#):
using System;
using System.Collections.Concurrent;
using System.Threading;
using System.Web.Mvc;
using Newtonsoft.Json;
namespace EventSourceTest2.Controllers {
public class PingData {
public int UserID { get; set; }
public DateTime Date { get; set; } = DateTime.Now;
}
public class HomeController : Controller {
public ActionResult Index() {
return View();
}
static ConcurrentQueue<PingData> pings = new ConcurrentQueue<PingData>();
public void Ping(int userID) {
pings.Enqueue(new PingData { UserID = userID });
}
public void Message() {
Response.ContentType = "text/event-stream";
do {
PingData nextPing;
if (pings.TryDequeue(out nextPing)) {
var msg = "data:" + JsonConvert.SerializeObject(nextPing, Formatting.None) + "\n\n";
Response.Write(msg);
}
Response.Flush();
Thread.Sleep(1000);
} while (true);
}
}
}
Once I've pressed ping to add a new item to the pings queue, the loop inside the Message method picks the new item up and issues an event, via Response.Write (confirmed using Debug.Print on the server). However, the browser doesn't trigger onmessage until I press ping a second time, and the browser issues another event; at which point the data from the first event reaches onmessage.
How can I fix this?
To clarify, this is the behavior I would expect:
Client Server
-------------------------------------------------------------------
Press Ping button
XHR to /home/ping
Eneque new item to pings
Message loop issues server-sent event
EventSource calls onmessage
This is what is actually happening:
Client Server
-------------------------------------------------------------------
Press Ping button
XHR to /home/ping
Eneque new item to pings
Message loop issues server-sent event
(Nothing happens)
Press Ping button again
New XHR to /home/ping
EventSource calls onmessage with previous event data
(While running in Chrome the message request is listed in the Network tab as always pending. I'm not sure if this is the normal behavior of server-sent events, or perhaps it's related to the issue.)
Edit
The string representation of the msg variable after Response.Write looks like this:
"data:{\"UserID\":105,\"Date\":\"2016-03-11T04:20:24.1854996+02:00\"}\n\n"
very clearly including the newlines.
This isn't an answer per say but hopefully it will lead one. I was able to get it working with the following code.
public void Ping(int id)
{
pings.Enqueue(new PingData { ID = id });
Response.ContentType = "text/plain";
Response.Write("id received");
}
public void Message()
{
int count = 0;
Response.ContentType = "text/event-stream";
do {
PingData nextPing;
if (pings.TryDequeue(out nextPing)) {
Response.ClearContent();
Response.Write("data:" + nextPing.ID.ToString() + " - " + nextPing.Date.ToLongTimeString() + "\n\n");
Response.Write("event:time" + "\n" + "data:" + DateTime.Now.ToLongTimeString() + "\n\n");
count = 0;
Response.Flush();
}
if (!Response.IsClientConnected){break;}
Thread.Sleep(1000);
count++;
} while (count < 30); //end after 30 seconds of no pings
}
The line of code that makes the difference is the second Response.Write. The message doesn't appear in the browser until the next ping similar to your issue, but the ping always appears. Without that line the ping will appear only after the next ping, or once my 30 second counter runs out.
The missing message appearing after the 30 second timer leads me to conclude that this is either a .Net issue, or there's something we're missing. It doesn't seem to be an event source issue because the message appears on a server event, and I've had no trouble doing SSE with PHP.
For reference, here's the JavaScript and HTML I used to test with.
<input type="text" id="pingid" placeholder="ID" /><br />
<input type="button" id="ping" value="Ping" />
<div id="timeresponse"></div>
<div id="pingresponse"></div>
<script>
var es = new EventSource('/Home/Message');
es.onmessage = function (e) {
console.log(e.data);
document.getElementById('pingresponse').innerHTML += e.data + " - onmessage<br/>";
};
es.addEventListener("ping", function (e) {
console.log(e.data);
document.getElementById('pingresponse').innerHTML += e.data + " - onping<br/>";
}, false);
es.addEventListener("time", function (e) {
document.getElementById('timeresponse').innerHTML = e.data;
}, false);
es.onerror = function () {
console.log(arguments);
console.log("event source closed");
es.close();
};
window.onload = function(){
document.getElementById('ping').onclick = function () {
var xmlhttp = new XMLHttpRequest();
xmlhttp.onload = function () {
console.log(this.responseText);
};
var url = '/Home/Ping?id=' + document.getElementById('pingid').value;
xmlhttp.open("GET", url);
xmlhttp.send();
};
};
</script>
Since an eventstream is just text data, missing the double line break before the first event is written to response could affect the client. The example from mdn docs suggests
header("Content-Type: text/event-stream\n\n");
Which could be applied apply to .NET response handling (note the side effects of Response.ClearContent()).
If it feels too hacky, you could start your stream with a keep-alive comment (if you want to avoid timing out you may have to send comments periodically):
: just a keep-alive comment followed by two line-breaks, Response.Write me first
I'm not sure if this will work because I can't try it now, but what about to add an End?:
Response.Flush();
Response.End();
The default behavior of .net is to serialize access to session state. It blocks parallel execution. Requests are processed sequentially and access to session state is exclusive for the session. You can override the default state per class.
[SessionState(SessionStateBehavior.Disabled)]
public class MyPulsingController
{
}
There is an illustration of this in the question here.
EDIT: Would you please try creating the object first and then passing it to Enqueue? As in:
PingData myData = new PingData { UserID = userID };
pings.Enqueue(myData);
There might be something strange going on where Dequeue thinks it's done the job but the the PingData object isn't properly constructed yet.
Also can we try console.log("I made it to the function") instead of console.log(e.data).
---- PREVIOUS INFORMATION REQUESTED BELOW ----
Please make sure that the server Debug.Print confirms this line of code:
Response.Write("data:" + JsonConvert.SerializeObject(nextPing, Formatting.None) + "\n\n");
Is actually executed? Please double check this. If you can capture the server sent response then can we see what it is?
Also could we see what browsers you've tested on? Not all browsers support server events.
I'm with a little problem on my project.
Hi have several jsp's and Java class. In one jsp i create a form with only a input type="file" and type="submit", then I have an ajax call and send all the formdata to a doPost class on my servel. Then I send that file to the DataBase and it all's go fine, my problem is I want to return the id from the database to the .jsp. I can access and have prints on the doPost to check my key, but cant send it to success function inside the ajax call..
Here's my code, i really apreciate any kind of help, thanks!
<form id="uploadDocuments" target="invisible" method="POST" action="UploadDocumentsAjaxService" enctype="multipart/form-data">
<iframe name="invisible" style="display:none;"></iframe>
<h3 style="width: 71%;margin-left: 8%;">ANEXAR FICHEIROS:</h3>
<h4 style="margin-left: 8%; color: #F7A707" >Escolher ficheiro para anexar: </h4>
<input type="file" id="file_input" name="file" size="50" style="width: 60%; margin-left: 8%;"/>
<input type="submit" value="Upload" />
</form>
the I have my Ajax Call:
$("#uploadDocuments").submit(function (e) {
alert(10);
alert($("#uploadDocuments").attr('action'));
$.ajax({
type: $("#uploadDocuments").attr('method'),
url: $("#uploadDocuments").attr('action'),
contentType: $("#uploadDocuments").attr( "enctype"),
data: new FormData($("#uploadDocuments")[0]),
processData: true,
success: function (data) {
alert("submitDocument");
alert();
/* key = data;
addFilesToTable(key); */
return true;
}
});
e.preventDefault();
$(form).off('submit');
return false;
});
And then my servlet class:
protected void doPost(HttpServletRequest request, HttpServletResponse response)throws ServletException, IOException{
response.setContentType("text/html;charset=ISO-8859-1");
PrintWriter out = response.getWriter();
boolean isMultipart = ServletFileUpload.isMultipartContent(request);
ChangeEntityRequestActionBean actionBean = new ChangeEntityRequestActionBean();
if(!isMultipart)
return;
// Create a factory for disk-based file items
DiskFileItemFactory factory = new DiskFileItemFactory();
// Sets the size threshold beyond which files are written directly to
// disk.
factory.setSizeThreshold(MAX_MEMORY_SIZE);
// constructs the folder where uploaded file will be stored
String uploadFolder = getServletContext().getRealPath("") + DATA_DIRECTORY;
// Create a new file upload handler
ServletFileUpload upload = new ServletFileUpload(factory);
// Set overall request size constraint
upload.setSizeMax(MAX_REQUEST_SIZE);
String fileName = "";
Long documentKey = null;
String key = "";
try {
// Parse the request
List items = upload.parseRequest(request);
Iterator iter = items.iterator();
while (iter.hasNext()) {
FileItem item = (FileItem) iter.next();
if (!item.isFormField()) {
fileName = new File(item.getName()).getName();
String filePath = uploadFolder + File.separator + fileName;
File uploadedFile = new File(filePath);
System.out.println(filePath);
// saves the file to upload directory
item.write(uploadedFile);
}
documentKey = actionBean.insertDocument(item, fileName);
System.out.println("Key from DAO ------->>>>>"+documentKey);
key = String.valueOf(documentKey);
}
System.out.println("Key in String from DAO ----->"+key);
System.out.println();
out.println("success");
response.flushBuffer();
}catch (FileUploadException ex) {
throw new ServletException(ex);
} catch (Exception ex) {
throw new ServletException(ex);
} finally {
out.close();
}
}
All I want is to send the key value to out.println so I can use that value on a jquery function
In the first line of doPost() in your servlet, change the content-type of the response to "application/json". Then write a JSON string to the output stream. There are libraries available to do this for you, but for something so simple, you can compose the JSON yourself. This might actually have an advantage because your key is a java long; treat it as a string and you don't have to worry about how the integer is represented.
// replace out.println("success"); with this:
out.print("{\"key\": \"" + key + "\"}");
Then in the success callback function, you can access the key as a field of the data object. You'll need to specify the data type in the ajax method (dataType: 'json').
success: function (data) {
var key = data['key'];
addFilesToTable(key);
return true;
}