Yi2 Download File Using SendFile() - javascript

I am attempting to use the built in function sendFile() provided by Yii2 to allow users to download files. This will not, however, actually download the file.
Below is my ajax code
$.ajax({
url: 'https://'+window.location.hostname+'/download',
dataType: "json",
type: 'POST',
data: {name: name},
})
Server side code
$filename = "test.txt";
$path = Yii::getAlias('#webroot')."/uploads/test.txt";
Yii::$app->response->sendFile($path, $filename)->send();
//I've also tried variations of the file path and name. E.G:
$filename = "test.txt";
$path = Yii::getAlias('#webroot')."/uploads";
The code provided above is what I am currently using to download the file. When a user clicks on a download icon, an Ajax call is made to the action containing the logic above, thus sending that file to the user's browser.
When the Ajax call is made, the server returns 200 but doesn't actually download the file. Instead, in the response is the content of the file being requested. For instance, if the user requests a file containing the text 'Hello there!', when the Ajax call is finished, nothing will be downloaded but the server response (as seen through FireFox dev tools) shows 'Hello there!'.
Is there any reason why the file itself isn't downloading?
If I just navigate to the url (lets say its localhost/downloadFile) in another tab, the action is called, the download dialogue opens, and I can download the file.

First thing you have to return the statement, and there isnt any use of calling send() after the sendFile() if you are returning it from the controller action, just keep it like below
return Yii::$app->response->sendFile($path, $filename);
Ajax isnt for file downloads you should either create a popup window or simply use
window.location.assign('https://'+window.location.hostname+'/download/'+name);
And you will see that the page wont change that you are currently on and the file download dialog will be triggered.

Related

How to download an attachment from an AJAX request?

I am developing a web application which uses a .NET MVC back end and HTML with JavaScript and AJAX on the front end.
One page has a feature where the page makes an AJAX call to the .NET controller which then returns a JSON object. Based on the contents of this object, the browser downloads a PDF file. This is implemented so that the same .NET controller has another method which returns a stream, and the JavaScript code then makes a full page request (not an AJAX call in this case) to that method, which causes the browser to offer to view or save the PDF file.
However, this seems like extra work, particularly since the PDF file is already available when the first AJAX call returns. I could just as easily dump its contents into the returned JSON object, encoded in Base64 if necessary. But my question is, how can I then trigger the download in the browser in this case?
Within your ajax callback, create an <a> tag with the href attribute set to the base64 string and then click on the tag.
.then((response) => {
var pdf = response.data;
if(!pdf) { return; }
var a = document.createElement("a");
a.href = `data:application/pdf;base64,${pdf}`;
a.download = "filename.pdf";
a.click();
})
Although I would recommend twitching your controller to make it return the pdf file directly with Content-Type set to application/pdf and replace the ajax request with a simple JavaScript statement like window.location="/api/pdf/username"

Need help understanding file uploads in Python Flask, w/ and w/o Javascript + Ajax [closed]

Closed. This question needs to be more focused. It is not currently accepting answers.
Want to improve this question? Update the question so it focuses on one problem only by editing this post.
Closed 5 years ago.
Improve this question
I am writing a webapp that lets the user upload photos, and then returns data about those photos to the front end to trigger some activity.
I started this off using a tutorial/guide I found online, here
I was however very surprised to find that this worked without javascript. I do not understand how pressing the button sends a request for there to be a pop-up window asking to select a file. How does it know that the button is for file uploading? No where in the HTML do I give it an obvious attribute that tells it to upload a file, and nowhere in python/Flask do I select the button used for uploading.
I also noticed, to my surprise, that in the example given, the route MUST be called "/upload" to work. Again, I can not see where any of this is defined or where I could change it. The only files used for the demo on that link (which is the basis of my webapp) are app.py and index.html (the setup.py file does not seem to be needed to run the webserver)
As my webapp has progressed, I tried to add a javascript file that would take a response from the /uploads route and display it on the front end, but soon ran into trouble. It seems I can only run /uploads from whatever mysterious force is currently running it. My javascript can't access it, and so I have no way of getting the response into JS so that I can display it on the front end in the way I want.
Can someone help me understand how the file upload server in the link works, so that I can modify it to work with my JavaScript?
Please note that my webapp is basically just the program in that link with a bunch of analysis done on the files that are uploaded. The only change I made in terms of how the webapp works is that instead of redirecting you to the uploaded file, it should return a string with information on that file.
I don't feel it is worth posting too much of my python and HTML code, since all the important stuff is in that link, but the javascript file is below.
"use strict";
var main = function() {
console.log("i am functioning!")
var show_similar_img = function(){
//Read megastatus
$.ajax({
method: "GET",
url: "/upload",
// contentType: 'application/json',
dataType: "string"
})
.done(function(media_info){
console.log(media_info)
});
}
$("#upload-btn").on("click",function(){
console.log("button pressed!")
show_similar_img()
})
}
console.log("i am being run!")
$(document).ready(main);
The <input> element's type attribute is file, which is how the browser knows how to handle it in the particular way that it does--as you noticed, by opening a pop-up modal window which prompts the user to select a file.
The route has to be called upload because the <form> element's action attribute value is also called upload. You could change the name of the route if you also changed the action of the form.
You can in fact access file information (for a file associated with the input element) using JavaScript thanks to the File API. Mozilla Developer Network has an excellent guide for it: https://developer.mozilla.org/en-US/docs/Using_files_from_web_applications
With regards to your actual implementation: first, you'll also want to prevent your form submission from being handled in the 'regular' (synchronous) way, so you'll need to call preventDefault on the event:
$("#upload-btn").on("click",function(event) {
event.preventDefault();
console.log("button pressed!")
show_similar_img()
})
Second, I am assuming that you want to actually send the file to the server (run on Python/Flask), so you want your AJAX request to be a POST rather than a GET. Therefore you'll need to modify your show_similar_img, to send the request as a POST and also to include the file in the data of the request, using the aforementioned File API. Something like this:
var show_similar_img = function(){
var formData = new FormData();
var file = $("input[type='file']")[0].files[0];
formData.append("file", file);
$.ajax({
url : "/upload",
type : "POST",
data : formData,
contentType: false,
processData: false
})
.done(function(media_info){
console.log(media_info)
});
}
Edit--I should add that I'm not really familiar with Flask or the intricacies of its request object. I assume that regardless of whether the file is uploaded as part of a form submission or asynchronous request that it would still be accessible at request.files['file'], but it is possible that Flask doesn't make asynchronously uploaded files available in the request.files dictionary. I am very sure, though, that the file would be accessible somewhere on the request object, so you may need to refer to the Flask documentation. You could also use pdb to add a breakpoint in your server-side code and inspect the request object to see what it holds (and where it holds it).

PHP to Javascript: dirname and document_root

I got a little problem. I am working on a project and I got my root, then I got 2 folders: website1 and website2.
website1 is the staff panel, where the upload script is on (where this problem is on). website2 is the website the 'customer' will see. Here all the uploaded images, PDFs etc. are uploaded to.
So, I have a few notes before I start:
I cannot use PHP in my Javascript. The Javascript is in .js files and not in my .php file.
I cannot do this at my AJAX request as something client-side has to be done correctly to see the image.
So basically, I am in website1 and when I upload a file (with DropzoneJS (http://www.dropzonejs.com/)) it does a AJAX request. Here it has to upload a file to a directory in website2. In website1 I have to see that image after uploading it in a IMG tag.
So in PHP I got this:
$uploadPath = filter_input(INPUT_POST, 'uploadPath');
$uploadPath = dirname(getenv('DOCUMENT_ROOT')) . '/' . $uploadPath;
This works and the file gets uploaded to that directory. $uploadPath is this (in Javascript, sent as POST parameter in AJAX request):
/SITE2/assets/uploads/
Now I need to translate the dirname and getenv('DOCUMENT_ROOT') into Javascript. I tried document.location.hostname but this does return a different value than getenv('DOCUMENT_ROOT').
document.location.hostname: 127.0.0.1
getenv('DOCUMENT_ROOT'): D:/xampp/htdocs
How can I get the same value as getenv('DOCUMENT_ROOT') in Javascript?
In php file you can create script with JavaScript variable like this:
<script>
var DOCUMENT_ROOT = "<?= getenv('DOCUMENT_ROOT') ?>";
</script>
the other option is to have your JavaScript file as PHP script then you can use PHP code inside.
You have two options, but I caution you sending the actual file path on the server/disk to the client side is a bad idea.
Use Ajax so the client sends a request like $.ajax({url: "getdir.php", success: someJSFuntion } );
Change your JavaScript file to be a ".php" file and then include the code right in there.
There is no spec that says you can't have your JS file be a ".php" file. So you'd link to it like this:
<script type="text/javascript" src="/js/myjs.php"></script>
And that would let you use PHP directly in your JS file.
Check out php.js for a JavaScript port of dirname...
http://phpjs.org/functions/dirname/
As for getenv('DOCUMENT_ROOT'), you can't get this variable value via JavaScript. The reason for this is the client-side "location" of the JavaScript file says nothing about it's actual location on the server. The best you can do is get the parent directory of the file or the domain name.
For example:
http://example.com/path/to/file
The "DOCUMENT_ROOT" as far as JavaScript is concerned is...
/path/to
It seems very improper to even assume the need to have the server-side location of the file available to the JavaScript. I would simply use either a specific URL query that indicates what location to use, or to send the location back with the AJAX response since you can add whatever you need there.
{
"document-root": "/SITE2/assets/uploads/",
"normal-response": {
...
}
}
And then wherever you would normally use what is normally received, this use...
// For example
var response = JSON.parse (request.responseText);
// Normal response
console.log (response['normal-response'][...]);
// Document Root
console.log (response['document-root']);

Get XHR sent formdata's output as a downloadable file

I'm doing a small HTML project of converting a doc file into another file (which is based on user preference). The website will have the user pickup the file, and choose their preferences on how the file should be processed, before sending it to a Java servlet. After processing the file, the servlet will then send the processed file as a downloadable file, which then will be automatically downloaded to the user's browser download folder.
At first, i used the HTML's form tag, with various input on it. After submitting the form, the processed file was auto-downloaded to my browser's download folder.
But then i changed the pickup file method into drag and drop, and used XMLHttpRequest to send the formdata, along with the dropped file to the servlet. The formdata was successfully sent to the servlet, and the servlet processed the file normally. But after that, the servlet didn't send the processed file to my browser as a downloadable file. I checked on the inspector, on the response under network tab, and it showed the processed file content, yet i didn't get the processed file like i would normally get using form tag method.
So i was wondering did i do something wrong in my XHR code below? I just want to get the processed file from the servlet as downloadable file, and not showing it on my page. The HTML form tag works fine and the servlet returned the processed file as auto-download file, but with XHR, the processed file was only shown in the response tab under network in the inspector, no downloadable file or whatsoever.
function formHandler(form) {
var formdata = new FormData(form);
formdata.append("inputFile", doc);
var xhr = new XMLHttpRequest();
xhr.open('POST', "excelServlet", true);
xhr.send(formdata);
}
Ok i get the answer for this already.
AJAX cannot download the response from server directly like form's submit() method. =.=''' The response will be shown in the inspector's response, but user won't be able to download it.

Why threre is no way to download file using ajax request?

In our application we need to implement following scenario:
A request is send from client
Server handles the request and generates file
Server returns file in response
Client browser displays file download popup dialog and allows user to download the file
Our application is ajax based application, so it would be very easy and convenient for us to send ajax request (like using jquery.ajax() function).
But after googilng, it turned out that file downloading is possible only when using non-ajax POST request (like described in this popular SO thread). So we needed to implement uglier and more complex solution that required building HTML structure of form with nested hidden fields.
Could someone explain in simple words why is that ajax requests cannot be used to download file? What's the mechanics behind that?
It's not about AJAX. You can download a file with AJAX, of course. However the file will be kept in memory, i.e. you cannot save file to disk. This is because JavaScript cannot interact with disk. That would be a serious security issue and it is blocked in all major browsers.
This can be done using the new HTML5 feature called Blob. There is a library FileSaver.js that can be utilized as a wrapper on top of that feature.
That's the same question I'd asked myself two days ago. There was a project with client written using ExtJS and server side realisation was on ASP.Net. I have to translate server side to Java. There was a function to download an XML file, that server generates after Ajax request from the client. We all know, that it's impossible to download file after Ajax request, just to store it in memory. But ... in the original application browser shows usual dialog with options open, save and cancel downloading. ASP.Net somehow changed the standard behaviour... It takes me two day to to prove again - there is no way to download file by request usual way ... the only exception is ASP.Net... Here is ASP.Net code
public static void WriteFileToResponse(byte[] fileData, string fileName)
{
var response = HttpContext.Current.Response;
var returnFilename = Path.GetFileName(fileName);
var headerValue = String.Format("attachment; filename={0}",
HttpUtility.UrlPathEncode(
String.IsNullOrEmpty(returnFilename)
? "attachment" : returnFilename));
response.AddHeader("content-disposition", headerValue);
response.ContentType = "application/octet-stream";
response.AddHeader("Pragma", "public");
var utf8 = Encoding.UTF8;
response.Charset = utf8.HeaderName;
response.ContentEncoding = utf8;
response.Flush();
response.BinaryWrite(fileData);
response.Flush();
response.Close();
}
This method was called from WebMethod, that, in turn, was called from ExtJS.Ajax.request. That's the magic. What's to me, I've ended with servlet and hidden iframe...
you can do this by using hidden iframe in your download page
just set the src of the hidden ifame in your ajax success responce and your task is done...
$.ajax({
type: 'GET',
url: './page.php',
data: $("#myform").serialize(),
success: function (data) {
$("#middle").attr('src','url');
},
});

Categories

Resources