I have an app where frontend is developed in angularjs and backend with symfony.
I need to have a route like: http://example.com/api/invoices/file?file=foo
So I have this inside my FileController:
/**
* Matches /invoices/file/{filename} exactly
*
* #Route("/invoices/file/{filename}", name="get_invoice_file")
*/
public function getInvoiceFileAction(string $filename, Request $request)
{
$path = $this->get('kernel')->getRootDir() . '/../web/uploads/invoices/' . $filename;
if (!file_exists($path)) {
return new Response('file not found', 404);
}
$file = file_get_contents($path);
$headers = [
'Content-Type' => 'application/pdf',
'Conteng-Length' => filesize($path)
];
return new Response($file, 200, $headers);
}
Inside my angularjs app I have this to get the response inside my frontend controller:
vm.getInvoices = function() {
vm.loading = true;
apiResolver.resolve('invoices.documents.file#get', { "file": vm.searchFile }).then(function(response) {
vm.loading = false;
var file = new Blob([response], {type: 'application/pdf'});
var fileURL = URL.createObjectURL(file);
vm.file = $sce.trustAsResourceUrl(fileURL);
});
};
Into my html I have this:
<embed ng-src="{{vm.file}}" style="width:200px;height:200px;"></embed>
When I render the page I see a 200response so the file exist but into the html I have an empty space instead of pdf file.
Inside embed tag there is this:
<embed style="width:200px;height:200px;" ng-src="blob:http://localhost:3000/d32d87d1-6582-42e3-85ae-dc174ca5a473" src="blob:http://localhost:3000/d32d87d1-6582-42e3-85ae-dc174ca5a473">
If I copy url inside a browser returns me that can't load file.
Backend and frontend are in different folder and the pdf CAN'T be viewed by a public link infact these pages are protected with jwt system.
How can I show inside my page the pdf?
What I'm doing wrong?
Make sure that JWT Authorization Token is passed in the request . If not , pass it in the Blob object.
If token is passed try replacing embed to object as mentioned below :
<object data="{{vm.file}}" style="width:200px;height:200px;" type="application/pdf"></object>
Related
I'm using Flask with one of my wtforms TextAreaFields mapped to Trix-Editor. All works well except for images using the built toolbar attach button.
I'd like to save the images to a directory on the backend and have a link to it in the trix-editor text. I'm saving this to a database.
I can make this work by adding an <input type='file'/>in my template like so:
{{ form.description }}
<trix-editor input="description"></trix-editor>
<input type="file"/>
and the following javascript which I found somewhere as an example.
document.addEventListener('DOMContentLoaded', ()=> {
let contentEl = document.querySelector('[name="description"]');
let editorEl = document.querySelector('trix-editor');
document.querySelector('input[type=file]').addEventListener('change', ({ target })=> {
let reader = new FileReader();
reader.addEventListener('load', ()=> {
let image = document.createElement('img');
image.src = reader.result;
let tmp = document.createElement('div');
tmp.appendChild(image);
editorEl.editor.insertHTML(tmp.innerHTML);
target.value = '';
}, false);
reader.readAsDataURL(target.files[0]);
});
// document.querySelector('[role="dump"]').addEventListener('click', ()=> {
// document.querySelector('textarea').value = contentEl.value;
// });
});
This saves the image embedded in the text. I don't want that because large images will take up a lot of space in the database and slow down loading of the editor when I load this data back into it from the database.
It is also ugly having the extra button when Trix has an attachment button in it's toolbar. So, I'd like to be able to click the toolbar button and have it upload or if that is too hard, have the built in toolbar button save the image embedded.
To save the images to a folder instead of embedded, the Trix-editor website says to use this javascript https://trix-editor.org/js/attachments.js
In this javascript I have to provide a HOST so I use
var HOST = "http://localhost:5000/upload/"
and I set up a route in my flask file:
#tickets.post('/_upload/')
def upload():
path = current_app.config['UPLOAD_DIRECTORY']
if request.method == 'POST':
if 'file' not in request.files:
flash('No file part')
return redirect(request.url)
file = request.files['file']
if file.filename == '':
flash('No selected file')
return redirect(request.url)
if file and allowed_file(file.filename):
filename = secure_filename(file.filename)
session["id"] = filename
file.save(os.path.join(path, filename))
return send_from_directory(path, filename)
I can select an image and it shows in the editor and it uploads to the directory on my backend as expected. But when I save the form the location of the image is not in in the document text (should be in there as something like <img src="uploads/image.png>
On the python console I see
"POST /_upload/ HTTP/1.1" 404 -
I can make this go away if I change the return on my route to something like return "200" But all the examples I have seen about uploading files have this or a render_template. I don't want to render a template so I'm using this although I don't really understand what it does.
I'm assuming I need to return something the javasript can use to embed the image link in the document. But I'm a total newbie (like you didn't figure that out already) so I don't know what to do for the return statement (assuming this is where the problem lies).
If anyone else is trying to figure this out this is what I ended up doing.
Still needs a but of tweaking but works.
First I modified the example javascript for uploading to use Fetch instead of XMLHttpRequest
const editor = document.querySelector('trix-editor');
(function() {
HOST = '/_upload/'
addEventListener("trix-attachment-add", function(event) {
if (event.attachment.file) {
uploadFileAttachment(event.attachment)
}
// get rid of the progress bar as Fetch does not support progress yet
// this code originally used XMLHttpRequest instead of Fetch
event.attachment.setUploadProgress(100)
})
function uploadFileAttachment(attachment) {
uploadFile(attachment.file, setAttributes)
function setAttributes(attributes) {
attachment.setAttributes(attributes)
alert(attributes)
}
}
function uploadFile(file, successCallback) {
var key = createStorageKey(file)
var formData = createFormData(key, file)
fetch(HOST, {method: 'POST', body: formData}).then(function(response){
response.json().then(function(data){
alert(data.file, data.status)
if (data.status == 204) {
var attributes = {
url: HOST + key,
href: HOST + key + "?content-disposition=attachment"
}
console.log(attributes)
successCallback(attributes)
}
})
})
}
function createStorageKey(file) {
var date = new Date()
var day = date.toISOString().slice(0,10)
var name = date.getTime() + "-" + file.name
return [day, name ].join("/")
}
function createFormData(key, file) {
var data = new FormData()
data.append("key", key)
data.append("Content-Type", file.type)
data.append("file", file)
return data
}
})();
Then modified my Flask route (which I'll refactor, this was just slapped together to make it work):
def upload():
path = current_app.config['UPLOAD_DIRECTORY']
new_path = request.form["key"].split('/')[0]
file_upload_name = os.path.join(path, request.form["key"])
print(file_upload_name)
upload_path = os.path.join(path, new_path)
if request.method == 'POST':
if 'file' not in request.files:
flash('No file part')
return redirect(request.url)
file = request.files['file']
if file.filename == '':
flash('No selected file')
return redirect(request.url)
if file and allowed_file(file.filename):
if not os.path.exists(upload_path):
os.mkdir(upload_path)
filename = secure_filename(file.filename)
session["id"] = filename
attachment = os.path.join(upload_path, filename)
file.save(attachment)
file.close()
os.rename(attachment, file_upload_name)
print(os.listdir(upload_path))
return jsonify({'file': attachment, 'status': 204})
return f'Nothing to see here'
Anyway, I hope that helps as it took me ages to figure out.
I want to load the PDF file dynamically and show on browser. PDF file is created on the fly when user clicks on button and the filename has timestamp in it. So i cannot give the PDF filename in the html code as shown below as it changes based on the timestamp(PDF file name is given along with the timestamp when it was created as shown in below spring controller).
Below is the code.
html code:
<div ng-controller="generatePDFController">
<button ng-click="generatePDF()">Re-Generate PDF</button>
<object data="C:/allFiles/PDFFiles/spreadDetails.pdf" type="application/pdf" width="100%" height="100%">
<iframe src="C:/allFiles/PDFFiles/spreadDetails.pdf" width="100%" height="100%" style="border: none;">
This browser does not support PDFs.
Download PDF
</iframe>
</object>
</div>
js code:
app.controller('generatePDFController', function($scope, MyService) {
$scope.generatePDF = function() {
MyService.createPDF().then(
function(response) {
$scope.pdf = response;
},
function(errResponse) {
});
}
});
//service call
_myService.createPDF = function() {
var deferred = $q.defer();
var repUrl = sURL + '/allDataGeneration/generatePDF.form';
$http.get(repUrl)
.then(
function(response) {
deferred.resolve(response.data);
},
function(errResponse) {});
return deferred.promise;
}
spring controller:
#RequestMapping(value = "/generatePDF", method = RequestMethod.GET)
public# ResponseBody List < MyDTO > generatePDF() {
List < MyDTO > response = service.getAllData();
//create PDF and write the response in it
createPDFFile(response);
return response;
}
void createPDFFile(List < MyDTO > res) {
String FILE_PATH = "C:\\allFiles\\PDFFiles\\spreadDetails";
String FILE_EXTENSION = "pdf";
DateFormat df = new SimpleDateFormat("MM-dd-yyyy hh-mm-ssa");
String filename = null;
try {
filename = FILE_PATH + df.format(new Date()) + "." + FILE_EXTENSION;
} catch (Exception e) {
e.printStackTrace();
}
File file = new File(filename);
System.out.println("-----filename------------ " + filename); //PDF file is created successfully
//spreadDetails07-13-2017 02-59-51PM ,when user clicks on GeneratePDF in UI, it hits this controller and generates the PDF
//logic to write the data inside PDF file
}
The above shown code is the complete flow of my sample application. Now when user clicks on Re-Generate PDF button, it comes to above mentioned spring controller creates a file with timestamp and writes the data in it.How to pass the newly created pdf filename to the html code <object data="C:/allFiles/PDFFiles/spreadDetails.pdf" .. so that when pdf file is created it dynamically loads and show on UI.
---EDITED---
Please see the above edited code. createPDF(List<MyDTO>) is a new method in which i'm creating a pdf file and writing the content. I will be reusing this method.
Try to follow these steps :
Change the signature of the Java method generatePDF() in order to return a String representing the name of your file. This gives you the possibility to pass the name of the file to your JavaScript ;
In your controller, do $scope.pdfName = response. This way the name of the file is store the variable $scope.pdfName ;
Last step, replace <object data="C:/allFiles/PDFFiles/spreadDetails.pdf" ...> by <object data="{$scope.pdfName}" ...>
This should work.
Marine
EDIT given your own edit :
Your method generatePdf() is incorrect : you wrote that it must return a List<MyDto> but the keyword return is nowhere.
Do you really need to return he object List<MyDto> ? In any case, you need to return the name of the file to be able to use it in your JavaScript. So, you have two solutions : either this method only returns a String representing the name of the PDF, or it returns an object with two fields, one String and one List<MyDto>. In this second cas, you will need to do
$scope.pdfName = response.fieldContainingTheNameOfTheFile.
Hello im working with code igniter and I try to send parameters from url into view to controller
My Controller
public function mainView($num=null) {
$this->load->view('header');
// $data = array('foo[' => 'Hello', 'bar' => 'world');
if (!isset($_SESSION[$this::jsondevices])) {
var_dump("Descargo");
$_SESSION[$this::jsondevices] = $this->restlib->consumirDominiosDeMoca($this->restmodel->modelUserDevices());
$_SESSION[$this::jsontags] = $this->restlib->consumirDominiosDeMoca($this->restmodel->modelTagsMoca());
}
var_dump("No descargo");
$data = $this->piemodel->mainValues($this, $_SESSION[$this::jsondevices], $_SESSION[$this::jsontags]);
$this->load->view('user/login/vistachart', $data);
$this->load->view('footer');
}
My js
window.location.href = "<?= base_url('home2/$1')?>";
And my route
$route['home2/(:num)'] = 'user/mainView/$1';
And the result
404 Page Not Found
But I try with $route['home'] = 'user/mainView';
Work's
What Im doing wrong?
Send your parameter like this in js:
window.location.href = "<?= base_url('home2/1')?>";
it redirects to user/mainView/1 where 1 is parameter.
Also don't forget to load url helper in application/config/autoload.php.
I am trying to saeve recorded file to the server.
For recording purpose I am using demos recorder
At the end of recording it gives me a blob-link to the recorded file.
So After googling a bit I found that I can use that bob url to save it.
Here is the link that talks about saving blobs.
After that I am trynig to get it and download to server.
1- I get the link to blob file
var data = document.getElementById("save").href
After that
I am using js code in my index.html file to send
blob url to php code.
JS code
<script>
function saveAudio(){
var req = null;
var url = "savefile.php";
var data = document.getElementById("save").href.toString();// document.getElementById("save").innerHTML;// = xhttp.responseText;; // you have to check how to get the data from your saveAudio() method
window.alert(data);
(window.XMLHttpRequest) ? req = new XMLHttpRequest() : (window.ActiveXObject) ? req = new ActiveXObject("Microsoft.XMLHTTP") : req = false;
req.open("POST", url, true);
req.setRequestHeader("Content-Type", "multipart/form-data");
if(data != null) //&& data != "")
{
req.setRequestHeader("Content-length", data.length);
req.send(data);
}}
</script>
PHP code
<?php
$save_folder = dirname(__FILE__) ."/js";
if(! file_exists($save_folder)) {
if(! mkdir($save_folder)) {
die("failed to create save folder $save_folder");
}
}
$key = 'filename';
$tmp_name = $_FILES["audiofile"]["tmp_name"];
$upload_name = $_FILES["audiofile"]["name"];
$type = $_FILES["audiofile"]["type"];
$filename = "$save_folder/$upload_name";
$saved = 0;
if(($type == 'audio/x-wav' || $type == 'application/octet-stream') && preg_match('/^[a-zA-Z0-9_\-]+\.wav$/', $upload_name) ) {
$saved = move_uploaded_file($tmp_name, $filename) ? 1 : 0;
}
//name is needed to send in the php file
?>
I get 2 errors while compiling in browser
1-refused to set unsafe header "Content-length".
2-POST savefile.php 500
I suppose that there is something wrong with php file!
How canI handle these errors and accomplish uploading task?
Is there any open source which allows direct uploading blob-url to server without using php?
I appreciate any help and suggestion!
Try removing the line:
req.setRequestHeader("Content-length", data.length);
XMLHttpRequest isn't allowed to set these headers because that would be a security vulnerability. The 500 is most likely a result of the request failure.
You can read about XMLHttpReqest here http://www.w3.org/TR/XMLHttpRequest/#the-setrequestheader%28%29-method
Here are some other threads, same issue:
Pass Blob through ajax to generate a file
How can javascript upload a blob?
I have a button and onclick it will call an ajax function.
Here is my ajax function
function csv(){
ajaxRequest = ajax();//ajax() is function that has all the XML HTTP Requests
postdata = "data=" + document.getElementById("id").value;
ajaxRequest.onreadystatechange = function(){
var ajaxDisplay = document.getElementById('ajaxDiv');
if(ajaxRequest.readyState == 4 && ajaxRequest.status==200){
ajaxDisplay.innerHTML = ajaxRequest.responseText;
}
}
ajaxRequest.open("POST","csv.php",false);
ajaxRequest.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
ajaxRequest.send(postdata);
}
I create the csv file based on the user input. After it's created I want it to prompt download or force download(preferably force). I am using the following script at the end of the php file to download the file. If I run this script in a separate file it works fine.
$fileName = 'file.csv';
$downloadFileName = 'newfile.csv';
if (file_exists($fileName)) {
header('Content-Description: File Transfer');
header('Content-Type: text/csv');
header('Content-Disposition: attachment; filename='.$downloadFileName);
ob_clean();
flush();
readfile($fileName);
exit;
}
echo "done";
But If I run it at the end of csv.php it outputs the contents of the file.csv into the page(into the ajaxDiv) instead of downloading.
Is there a way to force download the file at the end of csv.php?
AJAX isn't for downloading files. Pop up a new window with the download link as its address, or do document.location = ....
A very simple solution using jQuery:
on the client side:
$('.act_download_statement').click(function(e){
e.preventDefault();
form = $('#my_form');
form.submit();
});
and on the server side, make sure you send back the correct Content-Type header, so the browser will know its an attachment and the download will begin.
#joe : Many thanks, this was a good heads up!
I had a slightly harder problem:
1. sending an AJAX request with POST data, for the server to produce a ZIP file
2. getting a response back
3. download the ZIP file
So that's how I did it (using JQuery to handle the AJAX request):
Initial post request:
var parameters = {
pid : "mypid",
"files[]": ["file1.jpg","file2.jpg","file3.jpg"]
}
var options = {
url: "request/url",//replace with your request url
type: "POST",//replace with your request type
data: parameters,//see above
context: document.body,//replace with your contex
success: function(data){
if (data) {
if (data.path) {
//Create an hidden iframe, with the 'src' attribute set to the created ZIP file.
var dlif = $('<iframe/>',{'src':data.path}).hide();
//Append the iFrame to the context
this.append(dlif);
} else if (data.error) {
alert(data.error);
} else {
alert('Something went wrong');
}
}
}
};
$.ajax(options);
The "request/url" handles the zip creation (off topic, so I wont post the full code) and returns the following JSON object. Something like:
//Code to create the zip file
//......
//Id of the file
$zipid = "myzipfile.zip"
//Download Link - it can be prettier
$dlink = 'http://'.$_SERVER["SERVER_NAME"].'/request/download&file='.$zipid;
//JSON response to be handled on the client side
$result = '{"success":1,"path":"'.$dlink.'","error":null}';
header('Content-type: application/json;');
echo $result;
The "request/download" can perform some security checks, if needed, and generate the file transfer:
$fn = $_GET['file'];
if ($fn) {
//Perform security checks
//.....check user session/role/whatever
$result = $_SERVER['DOCUMENT_ROOT'].'/path/to/file/'.$fn;
if (file_exists($result)) {
header('Content-Description: File Transfer');
header('Content-Type: application/force-download');
header('Content-Disposition: attachment; filename='.basename($result));
header('Content-Transfer-Encoding: binary');
header('Expires: 0');
header('Cache-Control: must-revalidate, post-check=0, pre-check=0');
header('Pragma: public');
header('Content-Length: ' . filesize($result));
ob_clean();
flush();
readfile($result);
#unlink($result);
}
}
I have accomplished this with a hidden iframe. I use perl, not php, so will just give concept, not code solution.
Client sends Ajax request to server, causing the file content to be generated. This is saved as a temp file on the server, and the filename is returned to the client.
Client (javascript) receives filename, and sets the iframe src to some url that will deliver the file, like:
$('iframe_dl').src="/app?download=1&filename=" + the_filename
Server slurps the file, unlinks it, and sends the stream to the client, with these headers:
Content-Type:'application/force-download'
Content-Disposition:'attachment; filename=the_filename'
Works like a charm.
You can't download the file directly via ajax.
You can put a link on the page with the URL to your file (returned from the ajax call) or another way is to use a hidden iframe and set the URL of the source of that iframe dynamically. This way you can download the file without refreshing the page.
Here is the code
$.ajax({
url : "yourURL.php",
type : "GET",
success : function(data) {
$("#iframeID").attr('src', 'downloadFileURL');
}
});
You can do it this way:
On your PHP REST api: (Backend)
header('Content-Description:File Transfer');
header('Content-Type:application/octet-stream');
header('Content-Disposition:attachment; filename=' . $toBeDownloaded);
header('Content-Transfer-Encoding:binary');
header('Expires:0');
header('Cache-Control:must-revalidate');
header('Pragma:public');
header('Content-Length:'.filesize($toBeDownloaded));
readfile($toBeDownloaded);
exit;
On your javascript code: (FRONTEND)
const REQUEST = `API_PATH`;
try {
const response = await fetch(REQUEST, {
method: 'GET',
})
const fileUploaded = await response.blob();
const url = window.URL.createObjectURL(fileUploaded);
const link = document.createElement('a');
link.href = url;
link.setAttribute('download', 'YOUR_FILE_NAME_WITH_EXTENSION');
document.body.appendChild(link);
link.click();
} catch (error) {
console.log(error)
}