MVC Upload control - asp.net-mvc

I am developing a MVC3 (razorview) application that makes use of the Telerik Controls.
So what I am doing is the following:
I want to upload a file, do some validation. If the file is valid I want to give the user the option to upload the file permanently. If it is not valid, the errors are displayed and the user can't upload the file.
This is what I have so far:
Javascript code:
<script type="text/javascript">
function onSuccess(e) {
if (jQuery.isEmptyObject(e.response.errors) && jQuery.isEmptyObject(e.response.warnings)) {
$("#successmsg").show();
} else {
var errorMsg = "";
if (!jQuery.isEmptyObject(e.response.errors)) {
for (var i = 0; i < e.response.errors.length; i++) {
errorMsg = errorMsg + "<li>" + e.response.errors[i] + "</li>";
}
}
if (errorMsg != "") {
var strErrorContent = $("#errormsg_template").html();
$("#errormsg").html(strErrorContent.replace("{0}", errorMsg));
$("#errormsg").show();
}
}
}
Telerik component in view:
<p>
#Html.Telerik().Upload().Name("file_upload").Multiple(false).Async(settings =>
{
settings.AutoUpload(false);
settings.Save("Test", "Pricat");
}).ClientEvents(events =>
{
events.OnError("onError");
events.OnSuccess("onSuccess");
events.OnUpload("onUpload");
})
</p>
So this code is working for the validation part. The user selects the file, the file is uploaded and some internal processing is validating the message.
If the message is valid, a successmessage is shown. If it not valid, the errors are displayed.
How should I proceed to add a button that does the upload (which is basically just another method call), without browsing to the file again?
So the following
settings.Save("Test", "Pricat");
should become
settings.Save("Upload", "Pricat");
So in the controller I have these 2 methods:
public ActionResult Upload(HttpPostedFileBase file_upload) { }
public ActionResult Test(HttpPostedFileBase file_upload) { }
What I had in mind:
In javascript, if the e.reponse doesn't contain errors, display a button (ASP.NET button, another upload telerik button, I don't know..).
When that button is clicked, it should call the "Upload" method in my controller. I just don't know how to send the file to that method
Does anyone know how to do this?
Or any other suggestions are more than welcome!
I hope it is a bit clear.
Thanks in advance.
Best regards.

It would be a very bad practice to upload the file twice in the first place. Not only would that use unnecessary bandwidth, you wouldn't really be able to guarantee the contents of the file stayed the same between requests.
Your 'Test' method, if successful, should save the file on the server somewhere in a 'validated but not permanently saved' state, and return a reference to the saved file to the client.
You would then pass that reference back into the 'Upload' action should the user decide to permanently save the file. (thus making it not actually involve a file upload at all)
You'll probably want to purge validated files which were never permanently saved every so often, but that's a small price to pay to do the process correctly.

If you insist on your solution you can upload a file with ASP.MVC using submit button like mentioned here: File Upload ASP.NET MVC 3.0
If you want it to be an AJAX post, you can use jQuery code like this (on onclick button event):
$.ajax('<%= Url.Action("Upload", "MyFileController") %>', {
async: async,
data: $('#myFileForm').serialize(),
success: function() { //something on success },
error: function() { //something on error },
type: 'POST',
timeout: 10000
}
But similary as #mootinator stated. I would better save the file on server (let's say in some temporary table or folder - or just flag it temporary in db with boolean value), validate it, ask your user if he want to save it permanently -> move the file to permanent folder or flag it permanent ... and in cases that it is not valid file or user refuses to save it permanently I would simply delete that file.

Related

Sent a file from backend to frontend. Cannot restore its old form

here I have a function that reacts to a button click
and gains a file from my backend.
onDownload() {
this.http.get('http://localhost:8080/backend/invoice/1/download',
{responseType: 'blob'})
.subscribe(res =>
console.log(res))
}
So far, I am happy because inside the chrome console I dont get any errors.
The response looks like this in the console:
The return type of the Java backend was InputStream (method annotation #Produces(MediaType.MULTIPART_FORM_DATA))
Then I found
https://stackblitz.com/edit/angular-blob-file-download?file=app%2Fapp.component.ts
and looked at ngOnInit() in app.component.ts
ngOnInit() {
const data = 'some text';
const blob = new Blob([data], { type: 'application/octet-stream' });
this.fileUrl = this.sanitizer.bypassSecurityTrustResourceUrl(window.URL.createObjectURL(blob));
}
Currently, I think my frontend receives a blob.
So, I can start in the line starting with "this.fileUrl="
and input my blob.
Inside the .html, I have a button to start the onDownload() function
and another tag to save the file on my local hard drive.
<div>
<button (click)="onDownload()">Herunterladen</button>
</div>
<a [href]="safeResourceUrl" download="file.txt">DownloadFile</a>
Meanwhile, I change the onDownload() method to
onDownload() {
this.http.get('http://localhost:8080/backend/invoice/1/download',
{responseType: 'blob'})
.subscribe(res => this.safeResourceUrl=this.sanitizer.bypassSecurityTrustResourceUrl(window.URL.createObjectURL(res)))
}
After I click "Herunterladen" and then the DownloadFile link I get either
a .txt file that I cannot read
or, if I change the file name to .pdf in the a tag inside the .html,
I get a "failed to load pdf document"
All I want is to get my original pdf that I stored in my database and that was sent from the backend.
Has anyone had the same problem before? Thank you for your help.
I changed my function to
onDownload() {
window.open(`http://localhost:8080/backend/invoice/${this.invoice.invoiceNr}/download`, 'blank');
}
Now it works:)

File download feature in grails application

I am looking to create a file on the fly and offer a download link to the user in a GRAILS application.
I followed the approach from here. I have no errors however it doesn't seem to work. Here's my controller code.
`render (file: pptFile, fileName:'someppt.pptx', contentType: 'application/octet-stream')
Client side code makes an AJAX call to retrieve the file from server. It does not cause the server to force downloading of the file on the client (browser). Here's the client side code.
$.ajax({
type : 'POST',
url : '<<URL>>',
success: function(result) {
var uri = 'data:application/octet-stream;charset=UTF-8,' +
encodeURIComponent(result);
window.open(uri, 'somePPT.pptx');
},
failure: function(){
alert ('failure')
}
});
Perhaps something akin to this (paraphrased, but used for downloading a json file):
def someControllerMethod() {
def dlContent = someService.marshalJson()
def contentType = "application/octet-stream"
def filename = "someFilename.json"
response.setHeader("Content-Disposition", "attachment;filename=${filename}")
render(contentType: contentType, text: dlContent as JSON)
}
okay. So I finally got this to work. As proposed by #railsdog and many others (This problem has been discussed on other threads in stackoverflow but the specific case I had was slightly different from those) I ended up writing to response directly from server and took out the AJAX call. The only reason I was doing an AJAX call was because I did not want to submit the current page that had the "generate file" functionality (There are many data elements on the page and I did not want to re-do the entire page just for downloading the file). So I ended up using an anchor tag with target as "_blank". Here's the code snippet
<a href="myControllerMethodToGenerateFileAndWriteToHTTPResponseDirectlyAsSuggestedByOthersInThisPost"
target="_blank"/>
This actually opened a new page and did the submission to initiate the download. Problem solved. It's working fine in CHROME. :) Thanks guys!
I like the solution using the render method from #railsdog !
A slightly other approach which I used so far was:
def controllerMethod() {
...
File file = sepaXmlService.createTransfersFile(...)
response.setContentType("application/xml")
response.setHeader("Content-disposition", "attachment;filename=${file.getName()}")
OutputStream out = response.getOutputStream()
out.write(file.bytes)
out.close()
file.delete()
return
...
}
In the view I use the following statement in the form:
<g:actionSubmit action="controllerMethod" class="btn" value="Get XML!" /></td>
I think it should also be possible to use a
<g:link controller="foobar" action="controllerMethod" class="btn">GetXML</g:link>

MVC Button Click performs action without redirecting

I have a table where users are allowed to "tick" or "cross" out a row. Ticking a row changes the status value to "Approved" and crossing it changes it to "Disapproved". I'm currently using the Edit scaffold to perform it. How do I do this without having the user being redirected to the view. I just want the user to click it and the page refreshes, with the status value being updated.
I'm not sure what code to post here either since I don't know how to write it. If any part of my program is required, please let me know. I'll include it here. Thank you :>
Add css classes to the 2 buttons "approve-btn" and "reject-btn".
Create javascript function to approve and reject and bind them to
the 2 classes
Create 2 backend functions
Make ajax calls from the JS functions to your backend functions passing the id of the row item
In the "success:" of the ajax call manage the change of the status to show "approved" or "rejected"
To make ajax call you can use the following example (although there are tons of example on google). Since you're modifying data you should use POST call and since it is a POST call, you should add a RequestVerificationToken to prevent CSRF attacks.
function Approve(id){
securityToken = $('[name=__RequestVerificationToken]').val();
$.ajax({
url: '/YourControllerName/Approve/' + itemId,
type: 'POST',
data: {
"__RequestVerificationToken": securityToken
},
success: function (data) {
if (data == 'success')
//use jQuery to show the approved message;
else
alert("something went wrong");
},
error: function (request, err) {
alert("something went wrong");
}
});
}
The Token should be created in the View adding this line:
#Html.AntiForgeryToken()

File download from JSF with a rendered response

I have some dynamically generated files which I want my JSF 2.0 app to download for the user. I've been able to get this working using the code found in the solution here :
Forcing a save as dialogue from any web browser from JSF application
and a command button in a form on the page
And that works fine except for one hitch. I'd like to be able to render a message back to the user on the initial page that tells them their file is being processed and to please wait. Obviously the responseComplete call stops that from happening. Is there some way to re-render the submitting page and send back a file from the same button?
No, you can't. You can send only one response back per request. Best what you could do is to use JavaScript to show an initially hidden div or something which contains the message during the onclick. But you'll have the problem that you cannot hide it whenever the download is completed.
An alternative is to store the file on temp disk and return a fullworthy JSF response wherein you display a download link which returns the file from temp disk by a standalone servlet.
I think you can use ajax to solve this. Call the method that creates the file from an ajax action and provide a javascript callback to handle the navigation or to show a layer or whatever
<script type="text/javascript">
function processEvent(data) {
if (data.status == "begin") {
showWaitingLayer();
} else if (data.status == "success") {
hideWaitingLayer();
showDownloadLink();
}
}
</script>
<h:commandLink action="#{myBean.createDocument}">
<f:ajax onevent="processEvent"/>
</h:commandLink>

Can you pick a browser target server-side?

I have a form that lets users select checks, and when submitted, creates a PDF, which opens in a new browser tab. It doesn't have any branding, and will probably open in a plugin anyway, so I don't want it taking over my site's tab. So I set the form's target to _blank.
But it's possible for the user to submit the form without enough information to create the PDF, in which case I flag the error (server-side) and re-render the form. But because I set the form's target, this re-render opens in a new tab as well, and that's not what I want - in this case, I want it to behave as if target were _top.
So the question is: Can I change the browser's rendering target server-side?
Yes, I know that this can be done with client-side JavaScript, but JS annoys me, and I have to do the validation server-side anyway. I may end up having to use it, but please don't suggest it as an answer - I'm more curious if what I'm attempting can even be done.
PS: I'm on Ruby on Rails 2.3.8, in case anyone knows a framework-specific solution.
A workaround on this problem would be to use the content-disposition header on the pdf, in order to force the file to be downloaded, and avoid the whole "target" approach..
Content-type: application/pdf
Content-Disposition: attachment; filename="downloaded.pdf"
No. This is a purely client-specific feature. As a matter of fact, it's quite possible to get a browser that supports only one window and where the target attribute would have simply no effect. There were even efforts to make this attribute disappear from future HTML standards completely (for instance, the XHTML branch had no such attribute).
The only overlap that I can think of between HTML and HTTP are the <meta http-equiv> tags (where HTML can affect otherwise HTTP-controlled behavior). HTTP is a transfer protocol, designed to work with about just any kind of data. Letting it control presentation would be a pretty terrible mix of concerns.
Fortunately, we live in a JavaScript-enabled world. It is rather easy to validate a form using an AJAX request, especially with libraries like jQuery.
For instance, this script performs a POST request to an URL (in this case, /pdf/validate) and expects the page to send back "ok" (if everything's good) or something else if there was an error.
<form method="post" action="/pdf/send" id="pdf-form">
<!-- form stuff here -->
</form>
<script type="text/javascript" src="jquery.js"></script>
<script type="text/javascript">
$(document).ready(function()
{
// set to true if we are to bypass the check
// this will happen once we've confirmed the parameters are okay
var programmaticSubmit = false;
// attach an event handler for when the form is submitted
// this allows us to perform our own checks beforehand; we'll do so by
// cancelling the event the user triggered, and do the submit ourselves if
// we detect no error
$('#pdf-form').submit(function(event)
{
if (!programmaticSubmit)
{
// first off, cancel the event
event.preventDefault();
// do an AJAX request to /pdf/validate
$.ajax("/pdf/validate", {
type: "POST",
data: $(this).serialize(), // send the form data as POST data
success: function(result)
{
// this gets called if the HTTP request did not end
// abnormally (i.e. no 4xx or 5xx status);
// you may also want to specify an "error" function to
// handle such cases
if (result == "ok")
{
// since the server says the data is okay, we trigger
// the event again by ourselves, but bypassing the
// checks this time
programmaticSubmit = true;
$(this).submit();
}
else // something went wrong! somehow display the error
alert(result);
}
});
}
});
});
</script>

Resources