Flask, serving Static File and or folder inside Javascript - javascript

I m trying to insert the following static url for a static folder inside a javascript so it can properly load a saved file, but i m still facing error.
Here is what happens.
the normal file location is http://localhost/static/uploads/filename.ext but with the following javascript, it fetch the location based on the views' url_prefix='/media' hence the url it fetches is http://localhost/media/static/uploads/filename.ext
here is the following code:
<script>
$(function(){
$('#fileupload').fileupload({
url: 'upload',
dataType: 'json',
add: function (e, data) {
data.submit();
},
success:function(response,status) {
console.log(response.filename);
var filePath = 'static/uploads/' + response.filename;
$('#imgUpload').attr('src',filePath);
$('#filePath').val(filePath);
console.log('success');
},
error:function(error){
console.log(error);
}
});
})
I m trying to replace,
var filePath = 'static/uploads/' + response.filename;
with
var filePath = {{ url_for('static', filename='/uploads/') }} + response.filename;
but with no success. The original filename settings leads to the Blueprint url_prefix, which i wanted to bypass.
Edit
Here is my Views
#media.route('/upload', methods=['GET', 'POST'])
def upload():
if request.method == 'POST':
file = request.files['file']
extension = os.path.splitext(file.filename)[1]
f_name = str(uuid.uuid4()) + extension
file.save(os.path.join(app.config['UPLOAD_FOLDER'], f_name))
return json.dumps({'filename':f_name})

There are two paths to consider here and you need to pay close attention to which you're using where:
the absolute filepath on the server eg: /opt/myapp/media/upload/<filename>, and
the relative urlpath on the client eg: https://localhost/static/upload/<filename>
Your easiest solution may be to simply return the filename, without any directory preamples, then prepend the appropriate directory for the context in which you use it.
So in the python you can still return 'somefile.jpg' with:
return json.dumps({'filename': f_name})
And in the javascript you can reference '/static/uploads/somefile.jpg' with:
var filepath = '/static/uploads/' + response.filename;

Well, I have fixed the issue, I have stripped the url_prefix parameters so i can call from the root url path. to avoid the previous issue.

Related

How do I fix my Blazor server app file download process?

I'm building Blazor server app .net5 , I'm using the following code for downloading files:
//After button click
else if(buttonName == "Download")
{
JSRuntime.InvokeVoidAsync("downloadFromUrl", new { Url = "api/files", FileName = "test.pdf" });
}
//this is the function for the download proccess
function downloadFromUrl(options) {
var _a;
var anchorElement = document.createElement('a');
anchorElement.href = options.url;
anchorElement.download = (_a = options.fileName) !== null && _a !== void 0 ? _a : '';
anchorElement.click();
anchorElement.remove();
}
//# sourceMappingURL=helper.js.map
The above half-works, I do start a download but the file I get downloaded is corrupt, the size of
the file is much smaller compared to the original file, no errors I can post here, I don't
understand what could be wrong, any ideas ?
I'm not sure using InvokeVoidAsync and a fake anchor is ideal. Here is another approach.
Create a middleware. Register it in your Startup.cs, above UseStaticFiles().
In the Invoke of the middleware, retrieve the rawUrl
string rawUrl = context.Request.Path.ToString() + context.Request.QueryString.ToString();
If in this rawUrl, you recognise an URL for a file download, then process it and return. Otherwise await _next(context);.
The process (where I wrote "process it") will be:
byte[] bytes = System.IO.File.ReadAllBytes("..."); // or anything else, e.g. the bytes come from a DB
context.Response.ContentType = "application/pdf"; // should be adapted to the file type
context.Response.Headers.Add("Content-Disposition", "attachment; filename=\"myFileName.pdf\"; size=" + bytes.Length.ToString());
context.Response.Body.WriteAsync(bytes);
In the HTML source, you don't have to create a button with a click handler. Just place an anchor, with an HREF recognized by the middleware.
Middleware info : https://learn.microsoft.com/en-us/dotnet/architecture/blazor-for-web-forms-developers/middleware, see Custom middleware

Non-ASCII characters are not correctly displayed in PDF when served via HttpResponse and AJAX

I have generated a PDF file which contains Cyrillic characters (non-ASCII) with ReportLab. For this purpose I have used the "Montserrat" font, which support such characters. When I look in the generated PDF file inside the media folder of Django, the characters are correctly displayed:
I have embedded the font by using the following code in the function generating the PDF:
from reportlab.pdfgen import canvas
from reportlab.lib.pagesizes import A4
from reportlab.pdfbase import pdfmetrics
from reportlab.pdfbase.ttfonts import TTFont
pdfmetrics.registerFont(TTFont('Montserrat', 'apps/Generic/static/Generic/tff/Montserrat-Regular.ttf'))
canvas_test = canvas.Canvas("media/"+filename, pagesize=A4)
canvas_test.setFont('Montserrat', 18)
canvas_test.drawString(10, 150, "Some text encoded in UTF-8")
canvas_test.drawString(10, 100, "как поживаешь")
canvas_test.save()
However, when I try to serve this PDF via HttpResponse, the Cyrillic characters are not properly displayed, despite being displayed in the Montserrat font:
The code that serves the PDF is the following:
# Return the pdf as a response
fs = FileSystemStorage()
if fs.exists(filename):
with fs.open(filename) as pdf:
response = HttpResponse(
pdf, content_type='application/pdf; encoding=utf-8; charset=utf-8')
response['Content-Disposition'] = 'inline; filename="'+filename+'"'
return response
I have tried nearly everything (using FileResponse, opening the PDF with with open(fs.location + "/" + filename, 'rb') as pdf...) without success. Actually, I do not understand why, if ReportLab embeddes correctly the font (local file inside media folder), the file provided to the browser is not embedding the font.
It is also interesting to note that I have used Foxit Reader via Chrome or Edge to read the PDF. When I use the default PDF viewer of Firefox, different erroneous characters are displayed. Actually the font seems to be also erroneous in that case:
Edit
Thanks to #Melvyn, I have realized that the error did not lay in the response directly sent from the Python view, but in the success code in the AJAX call, which I leave hereafter:
$.ajax({
method: "POST",
url: window.location.href,
data: { trigger: 'print_pdf', orientation: orientation, size: size},
success: function (data) {
if (data.error === undefined) {
var blob = new Blob([data]);
var link = document.createElement('a');
link.href = window.URL.createObjectURL(blob);
link.download = filename + '.pdf';
link.click();
}
}
});
This is the part of the code that is changing somehow the encoding.
Solution with the ideas from comments
I finally come up with a solution thanks to all the comments I have received, specially from #Melvyn. Instead of creating a Blob object, I have just set the responseType of the AJAX to Blob type. This is possible since JQuery 3:
$.ajax({
method: "POST",
url: window.location.href,
xhrFields:{
responseType: 'blob'
},
data: { trigger: 'print_pdf', orientation: orientation, size: size},
success: function (data) {
if (data.error === undefined) {
var link = document.createElement('a');
link.href = window.URL.createObjectURL(data);
link.download = filename + '.pdf';
link.click();
}
}
});
Handling an error when returning response
You can return an error from Python (i.e. catching an exception) as follows:
except Exception as err:
response = JsonResponse({'msg': "Error"})
error = err.args[0]
if error is not None:
response.status_code = 403 # To announce that the user isn't allowed to publish
if error==13:
error = "Access denied to the PDF file."
response.reason_phrase = error
return response
Then, you just have to use the native error handling from AJAX (after the success section):
error: function(data){
$("#message_rows2").text(data.statusText);
$('#errorPrinting').modal();
}
See further details in this link.
I hope this post helps people with the same problem while generating PDFs in non-ASCII (Cyrillic) characters. It took me several days...
You are doing some encoding/recoding, because if you look at the diff between the files, it's littered with unicode replacement characters:
% diff -ua Cyrillic_good.pdf Cyrillic_wrong.pdf > out.diff
% hexdump out.diff|grep 'ef bf bd'|wc -l
2659
You said you tried without setting the encoding and charset, but I don't think that was tested properly - most likely you saw an aggressively browser-cached version.
The proper way to do this is to use FileResponse, pass in the filename and let Django figure out the right content type.
The following is a reproducible test of a working situation:
First of all, put Cyrillic_good.pdf (not wrong.pdf), in your media root.
Add the following to urls.py:
#urls.py
from django.urls import path
from .views import pdf_serve
urlpatterns = [
path("pdf/<str:filename>", pdf_serve),
]
And views.py in the same directory:
#views.py
from pathlib import Path
from django.conf import settings
from django.http import (
HttpResponseNotFound, HttpResponseServerError, FileResponse
)
def pdf_serve(request, filename: str):
pdf = Path(settings.MEDIA_ROOT) / filename
if pdf.exists():
response = FileResponse(open(pdf, "rb"), filename=filename)
filesize = pdf.stat().st_size
cl = int(response["Content-Length"])
if cl != filesize:
return HttpResponseServerError(
f"Expected {filesize} bytes but response is {cl} bytes"
)
return response
return HttpResponseNotFound(f"No such file: {filename}")
Now start runserver and request http://localhost:8000/pdf/Cyrillic_good.pdf.
If this doesn't reproduce a valid pdf, it is a local problem and you should look at middleware or your OS or little green men, but not the code. I have this working locally with your file and no mangling is happening.
In fact, the only way to get a mangled pdf now is browser cache or response being modified after Django sends it, since the content length check would prevent sending a file that has different size then the one on disk.
JS Part
I would expect the conversion to happen in the blob constructor as it's possible to hand a blob a type. I'm not sure the default is binary-safe.
It's also weird your data has an error property and you pass the entire thing to the blob, but we can't see what promise you're reacting on.
success: function (data) {
if (data.error === undefined) {
console.log(data) // This will be informative
var blob = new Blob([data]);
var link = document.createElement('a');
link.href = window.URL.createObjectURL(blob);
link.download = filename + '.pdf';
link.click();
}
}
For those who are doing form validation in views, you need to add below code in js file as return type is expected as blob.
xhr: function() {
var xhr = new XMLHttpRequest();
xhr.onreadystatechange = function() {
if (xhr.readyState == 2) {
if (xhr.status == 200) {
xhr.responseType = "blob";
}
}
};
return xhr;
},
success: function (response, textStatus, jqXHR) {
var blob = new Blob([response])
var link=document.createElement('a');
link.href=window.URL.createObjectURL(blob);
link.download="contract.pdf";
link.click();
},
error: function (response, textStatus, jqXHR) {
$('#my_form').click();
}

How Django and JavaScript work together for running Python script?

I am wondering how to accomplish the below with Django:
Consider a function in JavaScript:
$('#button').click(function(){
var rand_val = Math.random();
// here some code sends this rand_val to a Python script in Django app
// as seen below, script is run and generates result
alert('This is the result ' + result);
})
The script in the Django app:
def my_func(rand_val):
# result = (some operations with rand_val)
return result
The Python script will be run in Django virtual environment.
With a button click, run a Python script and show the result on the page.
You can send the data with ajax get,
var url = "url_to_my_func_view/" + rand_val
$.get( url, function( data ) {
alert( "Data Loaded: " + data );
});
You will need to return your result as bytes or JSON using HttpResponse or JsonResponse.
Edit:
If you need to send back user input (as the OP explained in the comments), then you are better off with GET or POST params. In JS you would do like so,
var url = 'url_to_my_func_view/'
$.get( url, {'user_val': rand_val}, function( data ) {
alert( "Data Loaded: " + data );
});
Then in your view you would catch those params like so,
def my_func(request):
user_val = request.GET['user_val']
return user_val
Notice that your function receives a request!
Hope this helps!
To perform form submission through Ajax, need to use some specific code snippets. I am adding the details source code so, you might find these helpful!
The code in JavaScript file:
//The functions below will create a header with csrftoken for Ajax based submission in Django application
function csrfSafeMethod(method) {
// these HTTP methods do not require CSRF protection
return (/^(GET|HEAD|OPTIONS|TRACE)$/.test(method));
}
function sameOrigin(url) {
// test that a given url is a same-origin URL
// url could be relative or scheme relative or absolute
var host = document.location.host; // host + port
var protocol = document.location.protocol;
var sr_origin = '//' + host;
var origin = protocol + sr_origin;
// Allow absolute or scheme relative URLs to same origin
return (url == origin || url.slice(0, origin.length + 1) == origin + '/') ||
(url == sr_origin || url.slice(0, sr_origin.length + 1) == sr_origin + '/') ||
// or any other URL that isn't scheme relative or absolute i.e relative.
!(/^(\/\/|http:|https:).*/.test(url));
}
$.ajaxSetup({
beforeSend: function(xhr, settings) {
if (!csrfSafeMethod(settings.type) && sameOrigin(settings.url)) {
// Send the token to same-origin, relative URLs only.
// Send the token only if the method warrants CSRF protection
// Using the CSRFToken value acquired earlier
xhr.setRequestHeader("X-CSRFToken", csrftoken);
}
}
});
// Ajax setup end
$('#button').click(function(e){
var rand_val = Math.random();
$.post('/my_func/', { rand_val: rand_val }, function(data){
return false;
});
e.preventDefault();
});
In views.py:
def my_func(request):
response = {}
if request.method == 'POST':
rand_val = request.POST.get('rand_val')
# result = (some operations with rand_val)
return result
In urls.py: Django v2.x
from django.contrib import admin
from django.urls import path, include
import APP_NAME.views
urlpatterns = [
path('my_func/', APP_NAME.views.my_func),
]
Expecting the code will work for you!

Load offline lang data in tesseract.js

I am trying to load my own trained data to tesseract.js. As the file is placed locally, I tried to load everything offline. The code I used is shown below:
<script src="tesseract.js"></script>
<script>
//Set the worker, core and lang to local files
(function() {
var path = (function() { //absolute path
var pathArray = window.location.pathname.split( '/' );
pathArray.pop(); //Remove the last ("**.html")
return window.location.origin + pathArray.join("/");
})();
console.log(path);
window.Tesseract = Tesseract.create({
workerPath: path + '/worker.js',
//langPath: path + '/traineddata/',
corePath: path + '/index.js',
});
})();
</script>
<script>
function recognizeFile(file){
document.querySelector("#log").innerHTML = ''
Tesseract.recognize(file, {
lang: document.querySelector('#langsel').value
})
.progress(function(packet){
console.info(packet)
progressUpdate(packet)
})
.then(function(data){
console.log(data)
progressUpdate({ status: 'done', data: data })
})
}
</script>
The code above is working fine if the langPath is not set, but when I point the langPath to a local folder, Tesseract failed to load anything with the following error:
Failed loading language 'eng'
Tesseract couldn't load any languages!
...
AdaptedTemplates != NULL:Error:Assert failed:in file ../classify/adaptmatch.cpp, line 190
SCRIPT0: abort() at Error
at Na (file:///C:/Users/user/Downloads/tesseract.js-master/dist/index.js:36:24)
at ka (file:///C:/Users/user/Downloads/tesseract.js-master/dist/index.js:511:83)
at Module.de._abort (file:///C:/Users/user/Downloads/tesseract.js-master/dist/index.js:377:166)
at $L (file:///C:/Users/user/Downloads/tesseract.js-master/dist/index.js:387:55709)
at jpa (file:///C:/Users/user/Downloads/tesseract.js-master/dist/index.js:392:22274)
at lT (file:///C:/Users/user/Downloads/tesseract.js-master/dist/index.js:391:80568)
at mT (file:///C:/Users/user/Downloads/tesseract.js-master/dist/index.js:391:80698)
at BS (file:///C:/Users/user/Downloads/tesseract.js-master/dist/index.js:391:69009)
at bP (file:///C:/Users/user/Downloads/tesseract.js-master/dist/index.js:387:110094)
at jT (file:///C:/Users/user/Downloads/tesseract.js-master/dist/index.js:391:80280)
at RJ (file:///C:/Users/user/Downloads/tesseract.js-master/dist/index.js:387:19088)
at QJ (file:///C:/Users/user/Downloads/tesseract.js-master/dist/index.js:387:17789)
at zI (file:///C:/Users/user/Downloads/tesseract.js-master/dist/index.js:403:90852)
at tw (file:///C:/Users/user/Downloads/tesseract.js-master/dist/index.js:401:49079)
at rw (file:///C:/Users/user/Downloads/tesseract.js-master/dist/index.js:401:48155)
at lw (file:///C:/Users/user/Downloads/tesseract.js-master/dist/index.js:401:39071)
at _v (file:///C:/Users/user/Downloads/tesseract.js-master/dist/index.js:401:22565)
at aw (file:///C:/Users/user/Downloads/tesseract.js-master/dist/index.js:401:24925)
at cw (file:///C:/Users/user/Downloads/tesseract.js-master/dist/index.js:401:27237)
at oj (file:///C:/Users/user/Downloads/tesseract.js-master/dist/index.js:386:24689)
at Og (file:///C:/Users/user/Downloads/tesseract.js-master/dist/index.js:386:10421)
at $.prototype.Recognize (file:///C:/Users/user/Downloads/tesseract.js-master/dist/index.js:558:379)
at Anonymous function (file:///C:/Users/user/Downloads/tesseract.js-master/dist/worker.js:8814:9)
at Anonymous function (file:///C:/Users/user/Downloads/tesseract.js-master/dist/worker.js:8786:9)
at xhr.onerror (file:///C:/Users/user/Downloads/tesseract.js-master/dist/worker.js:8429:9)
If this abort() is unexpected, build with -s ASSERTIONS=1 which can give more information.
index.js (8,1)
I have both eng.traineddata and eng.traineddata.gz in the /traineddata folder as apparently the ungzip process is skipped. Is there anything I neglected? Any help is appreciated.
I know this question is an old but recently I needed to use Tesseract.js in one of my projects. I needed to load Data Files locally so here is what I have done.
Instead of creating a new worker. I modified the default worker options available. So I didn't use Tesseract.createWorker and directly set the path and used recognize instead.
Tesseract.workerOptions.langPath =
window.location.origin // take protocol://domain.com part
+ "/scripts/tesseract/dist/"; // location of data files
//you could set core and worker paths too but I didn't need it
Tesseract.workerOptions.workerPath =
window.location.origin // take protocol://domain.com part
+ "/scripts/tesseract/dist/worker.js"; // location of worker.js
//you could set core and worker paths too but I didn't need it
Tesseract.workerOptions.corePath =
window.location.origin // take protocol://domain.com part
+ "/scripts/tesseract/dist/index.js"; // location of index.js
//example lang path would be protocol://domain.com/scripts/tesseract/dist/
By doing this, I left the worker and core paths untouched pointing to Default CDN.
PS: When using local worker.js and core.js paths I was getting uncaught error on postMessage() in worker.js. That's why I am using local path for langData only. I still don't know how to fix it or why it is happening. But, You can follow this issue here and here
I solved the problem by taking the corePath file from tesseract.js-core 0.1.0
window.Tesseract = Tesseract.create({
workerPath: window.location.origin + "/tesseract/worker.js", //tesseract.js-1.0.10
langPath: window.location.origin + "/tesseract/",
corePath: window.location.origin + "/tesseract/index.js", //tesseract.js-core-0.1.0
});
And language gz from https://github.com/naptha/tessdata/tree/gh-pages/3.02

Plupload file rename

I am using Plupload to upload to S3; My problem is that I want to change names of files, so when they reside in S3 they will be changed to a format I want. I managed to retrieve the file name of files uploaded by the function:
FilesAdded: function (up, files) {
for (var i in files) {
files[i].name = files[i].name.split('_').join(' ').trim();
alert('Selected files: ' + files[i].name);
}
the file name changes in the control but when I check the S3 the file is unchanged.
I made sure unique_names property is false and rename property to true; but did not work any help?
I faced the same problem i.e. using pluploader for S3 and wanted to normalize file names before uploading. Unfortunately 'Files' param appears to be read-only and any changes to file's name doesn't show up in the submitted form.
But if we change the 'key' param to an exact string (normalized name) it will cause S3 to save it with a different name. We can create a normalized name and use it in the 'key' param in the multipart_param in the 'FileAdded' callback.
....
FilesAdded: function(up, files) {
console.log("Uploader.FilesAdded ", up, files);
var file = files[0];
//Replace unwanted characters with "_"
var new_name = file.name.replace(/[^a-z0-9\.]+/gi,"_").toLowerCase();
console.log("Changing file name to ", file.name);
//Create multipart_params here (or just set the 'key' and 'Filename')
var multipart_params = {
'key': config.key + new_name, // *instead of ${filename}
'Filename': config.key + new_name,
'acl': config.acl,
'Content-Type': '',
'AWSAccessKeyId' : config.accessId,
'policy': config.policy,
'signature': config.signature
};
//
up.settings.multipart_params = multipart_params;
}
....
I had the requirement to add a user-definable prefix to all the files in the upload queue just before executing the multipart upload to S3. In my case, the prefix could be edited at any time before starting the upload - so using the FilesAdded event wasn't acceptable.
The solution that worked for me was to use the BeforeUpload which fires for each file in the queue just before starting the upload.
BeforeUpload: function(up, file) {
//Change the filename
file.name = appendUniqueId(file.name);
file.name = addPrefix(file.name);
var params = up.settings.multipart_params;
params.key = file.name;
params.Filename = file.name;
}
Try with unique_names:true with rename:true. It works for me.
The only fully working solution which I have found to this problem is to make use of the property multipart_params on the Plupload uploader.
The code I am using is;
uploader.bind('BeforeUpload', function (up, file) {
uploader.settings.multipart_params = { 'renamedFileName': 'yourRenamedFileName' };
});
Then when you are processing your image, you can then request this from the posted information;
c# code
var renamedFileName = context.Request["renamedFileName"].ToString();
For your code you could try;
uploader.bind('BeforeUpload', function (up, file) {
uploader.settings.multipart_params = { 'renamedFileName': file.name.split('_').join(' ').trim(); };
});
You can then use this additional information to rename the file on your server.

Categories

Resources