Django/JS - Send file to users, as known as download functionality - javascript

I want to let users to download a specific file, by clicking on a button "Download". The button will be linked with many switchers, so I wrote a JS script that change the "href" tag to point to the correct static file.
I tried to follow many stackoverflow questions and read documentation about Django staticfiles, media files but did not understand what I need to do on my case. Any help would be really appreciated, let me please introduce what I did and ask for your help/opinion.
I want to let people download files that can be found in :
"/home/user/xxxx/xxx/project/my_app/static/"
Here is my function in views.py :
def send_file(request,file_name):
from django.contrib.staticfiles.storage import staticfiles_storage
import os
from wsgiref.util import FileWrapper
import mimetypes
filename = staticfiles_storage.url(file_name)
download_name = file_name
wrapper = FileWrapper(open(filename))
content_type = mimetypes.guess_type(filename)[0]
response = HttpResponse(wrapper, content_type=content_type)
response['Content-Length'] = os.path.getsize(filename)
response['Content-Disposition'] = "attachment; filename=%s" % download_name
return response
I need the exact path to open the file, so what I have done is that I defined on my settings STATIC_URL = "/home/user/xxxx/xxx/project/my_app/static/"
. I do not like this solution, because after it, if you check my source, you have the exact path of my project. If I defined STATIC_URL = "static/" it does not work. I looked for a way to get exact path for the static file but it did not work. Any help for this part ?
urls.py:
url(r'^download/static/(?P<file_name>[\w.]{0,256})$',views.send_file, name='send_file'),
template.html, only the button part :
Download
JS, only the part that when you click on a switcher, it changes the href tag of the HTML button :
"if you click on a switcher"
id = switcher-checked
var atag = document.getElementById("dl-link");
var url = "http://localhost:8000/test/download/static/"+id+".csv";
atag.setAttribute("href",url);
Is there a solution to use the {% url 'my_app:send_file %} tag in my JS ? I found that one solution is to put the script directly within "template.html", is it a good behaviour ?
My downloading is working perfectly, but I feel like all my choices are pretty bad (STATIC_URL and JS var url definition). I know that my question is quite dense, but I really need this help. Any examples would be more than appreciated. Thank you.

Couple of points. first one for STATIC_URL you can use "/static/" with starting and ending with slash instead of complete path. since you are trying download from same domain name no need to use complete URL "http://localhost:8000/your-url". You can simply use "/your-url" in js var url variable.
Django cannot render js files, so we have only one option, keeping it in template.html. For better maintaining of js code, try declaring global variable with urls in the template on document load and use them in your script. So that script part will be clear. for ex,
var all_my_urls = {
"url":"{% url 'my_app:send_file'%}"
}

Related

Flask url_for doesn't work in template's external JavaScript

In index.html, I link to an index.js file. On click one button, js sends a request to Flask back end. The backend returns a static file path: 'data/Sharon_4.png'. I want to render it in HTML using the following function, but it doesn't work. To simplify it, I replaced the URL with a specific URL as the following, not as a variable. It still doesn't work.
function test(){
var mvt = document.getElementById('movieThumbnail')
var ig = document.createElement('img')
ig.src = `{{url_for('static', filename='data/haron_4.png')}}`
mvt.append(ig)
}
In HTML the tag seems right <img src="{{url_for('static', filename='data/Sharon_4.png')}}">
If I put this tag directly in HTML or in in-page script, it works. But here using url_for in js it doesns't.
Jinja2 template processor as commonly employed in Flask apps, only works on template files. You are importing JavaScript via the <script> element. The template processor won't see that JavaScript. If you place JavaScript directly into your HTML file it will be processed by Jinja2. Example:
<script>
function test(){
var mvt = document.getElementById('movieThumbnail')
var ig = document.createElement('img')
ig.src = `{{url_for('static', filename='data/haron_4.png')}}`
mvt.append(ig)
}
</script>
What you could do is use this simple script to store the static folder in a window variable and use that in your script. Example:
<script>
window.static_folder = "{{url_for('static')}}";
</script>
And then refer to the global var in your script. Crude Example:
function test(){
const mvt = document.getElementById('movieThumbnail');
const ig = document.createElement('img');
ig.src = `${window.static_folder}/data/haron_4.png`;
mvt.append(ig);
}
Alternative you can call an api on your Flask server to get an url_for. Please see this example:
#bp.route('/url_for/')
def public_get_url_for():
"""
get the url using the url_for method. url parameters are given as request args
ie: /url_for?endpoint=stock_edit&stock_id=12
example:
$.get({
url: '/url_for',
data: {endpoint: 'stock_edit', stock_id: $('#stock_id').val()}
}).then(url => window.location = url);
for route:
#app.route('/stock_edit/<int:stock_id>')
def stock_edit(stock_id):
# some code
:return: the url or 404 if error
"""
keywords = request.args.to_dict()
try:
return url_for(**keywords)
except Exception as e:
return str(e), 404
I realize this is now a bit of an old question, but I came across it while investigating whether there was a "flask" way to do this, so I though I'd include how I resolved the problem.
If, like me, your javascript files include a bunch of ajax api calls and you don't want to run the risk of breaking them if you make changes, and you also don't want javascript in every template file or one massive block of inline javascript in the layout template, then this might work for you:
Create a route for the path "/js", that takes a filename as an argument, then returns a render of the filename specified.
from flask import Blueprint, abort, render_template
js = Blueprint('js', __name__, url_prefix='/js')
#js.route('/<filename>', methods=['GET'])
def file(filename):
# Prevent attempts to go outside of the js directory or return a different type of file
if "../" in filename or ".js" not in filename:
abort(404)
else:
# Go ahead and try to return the file, if it doesn't exist, return a 404
try:
return render_template("js/" + filename)
except:
abort(404)
app.register_blueprint(js.js)
Then create a folder called "js" in the templates directory of your flask project, and move all of the javascript files that require jinja to that directory.
/ {root}
|____templates
|____layout.html
|____some_other_template.html
|____js
|____myJSFile.js
Then in your templates you can use script tags that look like this:
<script src="{{ url_for('js.file', filename='myJSFile.js') }}" referrerpolicy="origin"></script>
At this point, while they appear in the html output to be being loaded like normal script links, they are actually being loaded as jinja templates, and you can use any jinja you like inside them.

set file attribute filesystemobject javascript

I have created a file as part of a script on a network drive and i am trying to make it hidden so that if the script is run again it should be able to see the file and act on the information contained within it but i am having trouble doing this. what i have so far is:
function doesRegisterExist(oFs, Date, newFolder) {
dbEcho("doesRegisterExist() triggered");
sExpectedRegisterFile = newFolder+"\\Register.txt"
if(oFs.FileExists(sExpectedRegisterFile)==false){
newFile = oFs.OpenTextFile(sExpectedRegisterFile,8,true)
newFile.close()
newReg = oFs.GetFile(sExpectedRegisterFile)
dbEcho(newReg.Attributes)
newReg.Attributes = newReg.Attributes+2
}
}
Windows Script Host does not actually produce an error here and the script runs throgh to competion. the only guides i have found online i have been attempting to translate from VBscript with limited success.
variables passed to this function are roughly declared as such
var oFs = new ActiveXObject("Scripting.FileSystemObject")
var Date = "29-12-2017"
var newFolder = "\\\\File-Server\\path\\to\\folder"
I know ActiveX is a dirty word to a lot of people and i should be shot for even thinking about using it but it really is a perfect fit for what i am trying to do.
Please help.
sExpectedRegisterFolder resolves to \\\\File-Server\\path\\to\\folder\\Register which is a folder and not a file.
I get an Error: file not found when I wrap the code into a try/catch block.
I tested the code on a text file as well, and there it works.
So you're either using the wrong method if you want to set the folder to hidden.
Or you forgot to include the path to the text if you want to change a file to hidden.
( Edit: Or if Register is the name of the file, add the filetype .txt ? )
If you change GetFile to GetFolder as described in https://msdn.microsoft.com/en-us/library/6tkce7xa(v=vs.84).aspx
the folder will get hidden correctly.

Accessing file with JS when files are a folder deeper than domain

We have a MVC site which uses subdomains. Not in the traditional sub.domain.com but instead we are using domain.com/sub. The source files all exist in the sub folders of each sub domain because each might have some slightly different things. This causes the Dev team to have to place JS directly into the razor pages so the razor code was able to update URLs like below.
var temp = $('div').load('#Url.Content("~/Images/Excel.png")');
Unfortunately using a code like below in a separate JS file tries loading from domain.com and not domain.com/sub
var temp = $('div').load('/Content/Templates/warning.html');
Theses add on to the domains and can change with clients. Is there a way to get the domain plus sub when the files are loaded like that in the JS without needing to place the code into the razor? I'd prefer a separation of concerns because we are loading scripts sometimes which aren't even used because of it.
what I always do when in similar situations is that I create a function in the main.js or whatever name your using for your shared js file, modify the URL in the function and use the function as the initiator:
in the main.js:
var loadFile = function(selector,path){
$(selector).load('/sub'+path);
}
and then whenever and wherever you wanna load a file:
var temp = loadFile('div','/Content/Templates/warning.html');
UPDATE
you can upgrade your loadFile function to let it know if it has to load from the root of the website if needed:
var loadFile = function(selector,path,loadFromRoot){
var root=(loadFromRoot) ? '' : '/sub';
$(selector).load(root+path);
}

My relative URL path to my View breaks on Deployment

I have a very standard ASP MVC app that I use a little javascript to show a Partial View. In order to make that Javascript work I needed to hard code a path to the Partial which is different between Dev and Production.
Mainly, in Dev there is no App specification where as in Production there is. See here:
Production=var URL = '/WetWashRequest/wetWashRequests/GetDetails?WONumber=' + wo;
Dev = var URL = '/wetWashRequests/GetDetails?WONumber=' + wo;
What this means is that as I work on it locally I delete the first part and when I want to deploy I have to remember to re add it.
This seems so ridiculously flawed that I can only assume I am being ignorant and doing something wrong...
You can take advantage of UrlHelper to get the URLs, as long as you do it in view:
var URL = '#Url.Action("GetDetails")';
Obviously, it doesn't make sense to put all your JavaScript in view, so what I will normal do is set just this in my view, in a namespace var, and then reference it in my external JavaScript:
View
<script>
var MyApplication = MyApplication || {};
MyApplication.GetDetailsUrl = '#Url.Action("GetDetails")';
</script>
External JS
$.get(MyApplication.GetDetailsUrl, { WONumber: wo }, function (result) {
...
});

Javascript Get Hostname of File Host

Although this question is similar, it is not what I am looking for.
Let's say on HostA.com I include a script from HostB.com:
<script type="text/javascript" src="http://www.hostb.com/script.js">
When script.js runs, I need to get the name of HostB (let's assume it can change). If I use:
var hostName = window.location.hostname;
It will return HostA.com rather than HostB.com obviously because that is where the the window object is scoped.
How can I get the name of HostB from within the script? Do I have to locate the <script> element in the DOM and parse the src attribute or is there a better way?
EDIT
Yes, it is on my server, but may be on other servers as well. I am developing a javascript plugin and am trying to make absolute paths so it doesn't try to reference files on the server including the plugin.
Here is how: first off, include this as the first line of your script. I know it is a comment. Do it anyways
//BLAHBLAHBLAHBLAHAAABBBCCCDDDEEEFFFGGGILIKEPI
next, use this function inside of that script to determine the host
function findHost(){
var scripts=document.getElementsByTagName('script');
var thisScript=null;
for(var i=0;i<scripts.length;i++){
if(scripts[i].innerHTML.indexOf('//BLAHBLAHBLAHBLAHAAABBBCCCDDDEEEFFFGGGILIKEPI')!==-1)
var thisScript=scripts[i];
}
var urlParser=document.createElement('a');
urlParser.href=thisScript.getAttribute('src');
return urlParser.hostname;
}
I am loading the script with RequireJS which looks something like this:
<script data-main="http://hostb.com/js/app/main.js" src="http://hostb.com/js/vendor/require.js" type="text/javascript"></script>
I figured out, with help from #adeneo that I can do something like this:
$('script[data-main*="/js/app/main.js"]').attr('data-main')
Which returns:
http://hostb.com/js/app/main.js
And I can parse it for the hostname.
var url = $('script[data-main*="/main.js"]').attr('data-main');
parser = document.createElement('a');
parser.href = url;
host = parser.hostname;
Thanks for the suggestions and nudge in the right direction!
BREAKING NEWS
Turns out their is an easier way for anyone using RequireJS (who finds this question in search) and needs to be able to load absolute URL's with the script host:
var myCssPath = require.toUrl('css/mystyles.css');
That builds an absolute path using the hostname of the server running!
To omit using the hostname twice (as you described in your 'accepted answer') I implemented the solution this as follows:
HTML on HostA.com:
<script data-main="my_embed_id" src="http://hostb.com/js/vendor/require.js" type="text/javascript"></script>
require.js on HostB.com:
// get host where this javascript runs
var url = $('script[data-main="my_embed_id"]').attr('src');
var hostb = url.replace(/(\/\/.*?\/).*/g, '$1');
Which returns:
http://hostb.com
Inspired by: How to make an external javascript file knows its own host?

Categories

Resources