Been struggling with this problem for a while.
I am trying to force a file download through javascript and PHP.
If I visit the PHP page directly it works but I want the download to start when on a different page but without success.
The URL to the file depends on a value taken from a SELECT item, however this part works as expected so I am leaving it out of the question.
Javascript
var xmlHttp = new XMLHttpRequest();
xmlHttp.open("GET", postUrl, true); //postUrl = http://www.example.com/downloadFile.php?url=http://www.example.com/files/test.eps
xmlHttp.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
xmlHttp.send();
PHP
$url = "http://www.example.com/files/test.eps";
$filesize= strlen(file_get_contents($url));
header('Content-Description: File Transfer');
header('Content-Type: application/force-download');
header('Content-Disposition: attachment; filename='.basename($url));
header('Expires: 0');
header('Cache-Control: must-revalidate');
header('Pragma: public');
header('Content-Length: ' . $filesize);
readfile($url);
I have been testing with both GET and POST (not sure which one would be best, feel free to correct me) but for now I am trying to get the GET to work.
Like I said, if I visit "http://www.example.com/downloadFile.php" directly the file is downloaded and if I do console.log(xmlHttp.responseText) the file contents is written to the console, but the file is not downloaded when making the request from the other page.
Any help would be greatly appreciated.
Turns out I used the link posted by #Vivasaayi, however I did it with a slight twist. Instead of using an Iframe I used a regular a href. PHP-file is unchanged.
Javascript
var downloadLinkID = 'hiddenDownloadLink', downloadHref = document.getElementById(downloadLinkID);
if (downloadHref === null)
{
downloadHref = document.createElement('a');
downloadHref.id = downloadLinkID;
downloadHref.setAttribute('href', postUrl + params);
downloadHref.setAttribute('onclick', firefoxDownload(postUrl + params)); // FF/IE must have onclick event to make link clickable
downloadHref.click();
}
function firefoxDownload(url)
{
window.location.href = url; //Needed for FF/IE to force download of file
}
Related
I'm using an API call (cURL, server-side) to receive an XML file. The XML file has a base64 encoded PDF embedded within, and I'm extracting this file from the XML, and then saving it on the server for later downloading.
I'm doing it like this:
<?php
$pdfContents = $content["DocumentHeader"]["DocumentPdf"]; // base64 string PDF
$endDocument = base64_decode($pdfContents);
$realName = 'some_random_string.pdf';
$filename = 'adjusted_name.pdf';
file_put_contents($realName,$endDocument);
header('Content-Description: File Transfer');
header('Content-type: application/octetstream');
header('Content-Disposition: attachment; filename='.$filename);
header('Connection: Keep-Alive');
header('Expires: 0');
header('Cache-Control: must-revalidate, post-check=0, pre-check=0');
header('Pragma: public');
header('Content-Transfer-Encoding: binary');
readfile($realName);
unlink($realName);
die();
?>
The front-end is receiving the file like so:
$.ajax({
url: 'get-file.php',
type: 'GET',
data: {},
success:function(data,status,xhr) {
var blob = new Blob([data], { type:'application/pdf' });
var downloadUrl = URL.createObjectURL(blob);
window.location.href = downloadUrl;
}
});
While this does download a PDF file, that file is broken. By broken, I mean that the contents are not entirely valid.
See the following image. On the right is the broken file, and on the left is what the file should display, when open in, say, Sublime Text 3 (or any other text editor).
I'm guessing I'm doing something wrong with the way I'm creating the Blob, or (not) processing the data, but I'm unsure what.
Here are some of the things I've tried:
Changing the header('Content-type') to application/pdf
Removing / commenting out the header('Content-Transfer-Encoding')
Using the TextEncoder and TextDecoder
Changing the front-end JS handling of data to:
var dataArray = new Uint8Array(data.length);
for (var i = 0; i < data.length; i++) {
dataArray[i] = data.charCodeAt(i);
}
var blob = new Blob([dataArray], { type:'application/pdf' });
You might ask why am I not simply passing the link to the PDF, and simulating a click on a hidden <a href>, or why am I not setting the responseType to blob, or why am I even using AJAX to begin with.
The answer to the first question is twofold - there are multiple simultaneous users (in the hundreds), and I'd like to avoid them knowing the real filename, and also to avoid keeping that file on the server for an indefinite period of time.
The second issue (responseType) - my response is not always a PDF file. It might be a JSON, containing a message for the user - the XML was not downloaded, there was an error while retrieving the parameters needed for the XML API call, etc, etc.
The third issue - I want this to work without the need for target blanks and wondering whether the user's browser will block the opening of a new tab, and I also want to keep the user on the page from which they initiated the download etc.
Finally, my question.
What do I need to change in the Blob creating part of my code, in order to get a functional PDF? I'm guessing it's an encoding issue, but I'm unsure how to go about fixing it.
EDIT
I've experimented with the conversion to blob, and after changing it to this:
var byteString = data;
var arrayBuffer = new ArrayBuffer(byteString.length);
var intArray = new Uint8Array(arrayBuffer);
for (var i = 0; i < byteString.length; i++) {
intArray[i] = byteString.charCodeAt(i);
}
var blob = new Blob([intArray], { type:'application/pdf' });
I still get a broken PDF, but with a slightly different encoding issue, like in the following image.
Managed to resolve the issue.
Despite my initial doubts, the problem was indeed on the server. Specifically, this part:
header('Content-Transfer-Encoding: binary');
readfile($realName);
I was sending the stream, which made it impossible for the front-end logic to convert it correctly to what I needed. The thing that gave it away (apart from the helpful comments) was logging the contents of xhr.responseText to the console - instead of the expected format of the PDF contents, I noticed that there were diamonds / improperly converted characters.
With that in mind, I changed my code like this:
<?php
$pdfContents = $content["DocumentHeader"]["DocumentPdf"]; // base64 string PDF
//$endDocument = base64_decode($pdfContents); <== not needed in this case
//$realName = 'some_random_string.pdf'; <== not needed in this case
$filename = 'adjusted_name.pdf';
//file_put_contents($realName,$endDocument); <== not needed in this case
header('Content-Description: File Transfer');
header('Content-type: application/octetstream');
header('Content-Disposition: attachment; filename='.$filename);
header('Connection: Keep-Alive');
header('Expires: 0');
header('Cache-Control: must-revalidate, post-check=0, pre-check=0');
header('Pragma: public');
//header('Content-Transfer-Encoding: binary'); <== not good in this case
//readfile($realName); <== not good in this case
echo $pdfContents; // <== base64 representation of the raw PDF contents
unlink($realName);
die();
?>
Then, in the front-end code, I made additional changes:
$.ajax({
url: 'get-file.php',
type: 'GET',
data: {},
success:function(data,status,xhr) {
//var blob = new Blob([data], { type:'application/pdf' });
var byteString = atob(data);
var intArray = new Uint8Array(byteString.length);
for (var i = 0; i < byteString.length; i++) {
intArray[i] = byteString.charCodeAt(i);
}
var blob = new Blob([intArray], { type:'application/pdf' });
var downloadUrl = URL.createObjectURL(blob);
window.location.href = downloadUrl;
}
});
This forces the download of the received PDF, which looks like expected upon opening.
I have a web app, where pdfs need to be generated dynamically. Data is gathered in javascript and send with a post request to the server. Here mpdf is used to generate a pdf. If I save the file locally: php $mpdf->Output($filename, \Mpdf\Output\Destination::FILE); it works.
But if I send it to the browser php $mpdf->Output($filename, \Mpdf\Output\Destination::DOWNLOAD); and take the output in the jquery callback to do the following (borrowed from https://nehalist.io/downloading-files-from-post-requests/):
jQuery.post(my_axax_url, data, function(data) {
var blob = new Blob([data], { type: 'application/pdf' });
var l = document.createElement('a');
l.href = window.URL.createObjectURL(blob);
l.download = 'test.pdf';
document.body.appendChild(l);
l.click();
});
The downloaded pdf is empty (blank page), and has corrupted author information (that makes it look, like an encoding problem). I ran https://www.datalogics.com/products/pdftools/pdf-checker/, which only gave me it the javascript generated pdf is a "Damaged document".
I hope, that this is an easy problem. I am used to php and text documents, not pdf.
Thanks!
Try adding the following to the start of your php script, it may be some sort of encoding issue:
ob_clean();
header('Content-type: application/pdf');
header('Content-Disposition: inline; filename="test.pdf"');
header('Content-Transfer-Encoding: binary');
header('Accept-Ranges: bytes');
I use PHP to get it, because I need to download a PDF file from another server. To download this file I need to be logged in and the client shouldn't have to log in there.
The problem is that the PDF file the user gets is just a white page.
On my server there is still content inside when i write it in a file (file_put_contents("test.pdf",$content);)
I tried to base64 encode it before I send it and recognized that the delivered data is still correct. So it fails at the decoding (atob(data)) or in the download function.
(I also tried utf8 encodings)
In the downloaded pdf file, many characters are exchanged with these boxes or question marks(��).
$result = curl_exec($ch);
file_put_contents("test.pdf",$result); //test.pdf contains correct document
echo base64_encode($result);
download(atob(data),"My.pdf") //data is answer of ajax request
function download(data, filename, type) {
var file = new Blob([data], {
type: type
});
if (window.navigator.msSaveOrOpenBlob) // IE10+
window.navigator.msSaveOrOpenBlob(file, filename);
else { // Others
var a = document.createElement("a"),
url = URL.createObjectURL(file);
a.href = url;
a.download = filename;
document.body.appendChild(a);
a.click();
setTimeout(function () {
document.body.removeChild(a);
window.URL.revokeObjectURL(url);
}, 0);
}
}
The problem is the implicit UTF8 encoding in the BLOB constructor.
More information here.
To display a pdf (or any other file type), you will have to set the content type and so on in response header for the browser to resolve it correctly and display. This is a small portion of a code I wrote for delivering pdf content to the user.
$file = '/home/public_html/DriveMode/DRIVES/java.pdf'; //path to file
$filename = 'filename.pdf';
header('Content-type: application/pdf'); //setting content type in header
header('Content-Disposition: inline; filename="' . $filename . '"');
header('Content-Transfer-Encoding: binary');
header('Accept-Ranges: bytes');
readfile($file); // reads and writes the file to output buffer
Hope this helps you and do let me know in comments if something is confusing here.
I would ike to find a solution for downloading a video/audio from a URL when I click on a HTML button.
One special point: The URL its external (so I do not have any chance to touch it) but I would like to specify the filename before the downloading starts.
I tried it over PHP, but im not sure if this method is the best/simpelst one. Because I have to define few headers, which are currently not working for a (mp4) file.
<?php
if (isset($_GET['title'], $_GET['link'])) {
$FileName = $_GET['title'];
$Link = $_GET['link'];
$ContentType = get_headers($Link, 1)["Content-Type"];
header('Content-disposition: attachment; filename="' . $FileName . '"');
header('Content-type: ' . $ContentType . '');
readfile($Link);
};
?>
Questions:
What do I wrong? I do always receive a 0kb file.
is there a more simple way to do it over JS or jquery?
What if its an Audio only URL that I want to download. Can I use same headers?
Looks like you've forgotten to include the protocol (ie https://) on those links. You'll also need to URL encode the parameter in your HTML so you don't lose any query parameters.
For example, to use https://example.com/example?test123, you'll want
href="download.php?link=https%3A%2F%2Fexample.com%2Fexample%3Ftest123"
Producing that encoded parameter can be done via...
urlencode() in PHP
<?php $link = 'https://example.com/example?test123&HERE-is-the-real-Content'; ?>
Download
encodeURIComponent() in JavaScript
let link = 'https://example.com/example?test123&HERE-is-the-real-Content'
let a = document.createElement('a')
a.href = `download.php?title=Whatever&link=${encodeURIComponent(link)}`
a.textContent = 'Download'
or, if you're building HTML strings (not recommended)...
const input_URL = 'https://...'
html += `Download`
Note, I'm using template literals in these JS examples.
You should also make sure the remote URL is valid. For example
$headers = get_headers($Link, 1);
if (strpos($headers[0], '200 OK') === false) {
http_response_code(404);
exit;
}
My Problem
So I have been trying to create a simple file upload system using JavaScript (XHR) and PHP, and I have come across a problem where once the file has been uploaded to the PHP handler, it always returns that $_FILES is not set.
Code
JavaScript (on the server www.example.com):
var file = document.getElementById("fileInput").files[0];
var formData = new FormData();
formData.append("track", file);
var xhr = new XMLHttpRequest();
xhr.open("POST", "//handle.example.com/uploads.php", true);
xhr.onload = function(){
if(xhr.status == 200)
// awesome, it worked
else
return console.error("Something went wrong.");
};
xhr.send(formData);
PHP (on the server handle.example.com):
header("Access-Control-Allow-Origin: http://www.example.com", false);
if(isset($_FILES["track"])){
$file = $_FILES["track"];
$fileTemp = $file["tmp_name"];
$fileSize = filesize($fileTemp);
if($fileSize <= 150000000)
$data = "Success"; // this is returned if a file is an image file
} else
$data = "File not set."; // this is returned if a file is an audio file
echo $data;
exit;
HTML (on the server www.example.com):
<form method="post" enctype="multipart/form-data" id="ulF_uF1">
<input type="file" name="file" accept="audio/x-aiff,audio/flac,audio/mpeg,audio/ogg,audio/wav" id="fileInput">
</form>
What I've Tried
Setting the upload_max_filesize and post_max_size to 150M and 151M respectively.
Changing the name of the field name
Changing $_FILES to $_POST
None of this has worked for me, and I can't seem to find another other viable solutions relating to my problem, so all help is appreciated.
UPDATE:
After some careful testing, I realised that my upload script is actually working perfectly. What it doesn't like is the file types that are being uploaded. For some reason, PHP (version 7.2.1 at least) does not like when I upload audio files; uploading image files or PDF's work fine.
xhr.setRequestHeader("Content-Type", "multipart/form-data");
The Content-Type header for a request formatted as multipart/form-data must include a boundary parameter to tell the recipient of the message where each new bit of data starts.
By manually providing the header without it, you make the request unparsable.
Remove that line. Allow XMLHttpRequest to generate the correct content-type header for you using the FormData object.