I need to implement a code where I can let the client to download one or more local files on the web server.
I have 3 files: file1.php, file2.js, file3.php with the following codes.
In file1.php:
<select name="file_list" class="myclass" id="f_list" style="height:25px; width: 280px">
<?php
foreach (new DirectoryIterator("$filesFolder") as $file)
{
if((htmlentities($file) !== ".") && (htmlentities($file) !== ".."))
{
echo "<option>" . htmlentities($file) . "</option>";
}
}
?>
</select>
<?php
echo "<input type=\"button\" value=\" Download \" onClick=\"downloadFile()\"/>";
?>
in file2.js
function downloadFile()
{
$("#activity").html("<img src=\"img.gif\" style=\"left: 590px;top: 74px;margin: auto;position: absolute;width: 32px;height: 32px;\" />");
$("#content").load("file3.php",
{
filename: $("#f_list").val()
});
}
file3.php
if(isset($_POST["filename"]))
{
$filename = $_POST["filename"];
/* testing string */
echo $filesFolder.$filename;
if(file_exists($filesFolder.$filename))
{
ob_start();
header("Content-Type: application/force-download");
header("Content-Type: application/octet-stream");
header("Content-Type: application/download");
header('Content-Description: File Transfer');
header('Content-Disposition: attachment; filename='.basename($filename));
header('Content-Transfer-Encoding: binary');
header('Expires: 0');
header('Cache-Control: must-revalidate');
header('Pragma: public');
header('Content-Length: ' . filesize($filesFolder.$filename));
readfile($filesFolder.$filename);
ob_end_flush();
exit;
}
}
I took this last PHP code from the readfile function PHP manual because the behaviour is exactly what I need. But when file3.php is executed, the content of the file (which is binary data) is printed out on the screen.
I think I am missing to implement some feature but I don't know what it could be.
How can I obtain the same result of the readfile PHP manual page?
Thank you in advance for your help.
File2:
You can't use .load() or similar methods to download a file.
Just use window.location.href = "file3.php?filename="+$("#f_list").val();
File3:
You can't output anything before calling header()s. Comment out echo on line 5.
In File 2 we are using GET instead of POST, replace $_POST with $_GET.
It might also help to not request the page using ajax, just use
window.location.href='/download/url';
If all headers are correct, it will open a download dialog without leaving the current page.
One way would be to try to open the file in a new tab/window:
function downloadFile()
{
window.open("file3.php?filename=" + encodeURIComponent($("#f_list").val()));
}
Because the result of the PHP page has headers indicating that the content is a download not a page, the browser will not actually navigate, but will instead offer a download box. Note that this method uses GET not POST, so you will have to change from $_POST to $_GET or $_REQUEST in file3.php. Also, make sure you remove your echo before the headers go out or this won't work.
Related
On my site, if a user chooses to download a file, I set a session variable called $step to "downloadEnd" and my PhP code downloads a template file using the following code:
if ($step == "downloadEnd") {
$file = "Template.xlsx";
header('Content-Description: File Transfer');
header('Content-Type: ' . $mime);
header("Content-Transfer-Encoding: Binary");
header("Content-disposition: attachment; filename=\"" . basename($file) . "\"");
header('Content-Transfer-Encoding: binary');
header('Expires: 0');
header('Cache-Control: must-revalidate');
header('Pragma: public');
readfile($file);
exit();
$step = "nextStep"; // THIS IS THE LINE THAT DOES NOT EXECUTE
}
if ($step == "nextStep") {
// New Information Rendered to the User
}
The above code works, EXCEPT, that the last line of the first does not appear to 'execute'. In other words, after the download is complete, I want the user to see a new page with new text which is in a separate if statement ... if ($step == "downloadStart") ... but it never gets there. I believe it is because I need to somehow 'trick' the server into thinking there has been another user POST from the browser to the server AFTER the file downloads so that PhP iterates through all the 'if' statements and renders the new information to the user. I cannot seem to find a way to either: (i) have PhP trigger a page refresh after the file is done downloading; or (ii) trick the server into thinking it needs to refresh the page once the file is done. Any help would be appreciated.
I should add that I know the exit() at the end stops the PhP script from executing, but if you omit that line of code, the .xlsx file will be corrupted. I should also add that I tried the alternative fopen($file), fread($file), fclose($file) and that too gave me a corrupted .xlsx download, which is a problem others appear to have encountered as evidenced by other posts on Stack Overflow.
I think I see what your problem is. By running exit() you tell PHP to stop what it's doing. Try the following code:
if ($step == "downloadEnd") {
$file = "Template.xlsx";
header('Content-Description: File Transfer');
header('Content-Type: ' . $mime);
header("Content-Transfer-Encoding: Binary");
header("Content-disposition: attachment; filename=\"" . basename($file) . "\"");
header('Content-Transfer-Encoding: binary');
header('Expires: 0');
header('Cache-Control: must-revalidate');
header('Pragma: public');
readfile($file);
$step = "nextStep"; // THIS IS THE LINE THAT DOES NOT EXECUTE
}
if ($step == "nextStep") {
// New Information Rendered to the User
}
Also, you might want to look into JavaScript for refreshing the page after a document has downloaded.
Edit
If you are wanting to show this like a "click link if file did not properly download" page would be then the following should work:
if ($step == "downloadEnd") {
$fileURL = "https://example.com/path/to/Template.xlsx";
echo('<script>window.open("' . $fileURL . '", "_blank");</script>');
$step = "nextStep";
}
if ($step == "nextStep") {
// New Information Rendered to the User
}
You can use something like ob_end() or ob_end_clean() but it would be better to put the new information on a separate page and redirect the user there with echo('<script>window.location.replace("new.php")</script>'). Hope that helped! Also, PHP is a server-side language and therefore cannot tell when a file has finished downloading on the client.
I want to download image file in php.
<?php
if(isset($_REQUEST["file"])){
$filepath = BASE_URL.'assets/uploads/save_template_images/template1_221594899972.png';
// Process download
if(file_exists($filepath)) {
echo $filepath;
exit;
header('Content-Description: File Transfer');
header('Content-Type: application/octet-stream');
header('Content-Disposition: attachment; filename="'.basename($filepath).'"');
header('Expires: 0');
header('Cache-Control: must-revalidate');
header('Pragma: public');
header('Content-Length: ' . filesize($filepath));
flush(); // Flush system output buffer
readfile($filepath);
die();
} else {
echo $filepath;
exit;
http_response_code(404);
die();
}
}
?>
In my index page, I have an anchor tag and if click on anchor tag then above code run. I am not showing anchor tag because, I put the $filepath static value in above code. When I run above code then it goes on else condition. I think, full path of project is not taking by above code. If I put image in same folder then it downloads.
First ensure allow_url_fopen setting in php.ini file is turned on. After that Use this code to download your file:
<?php
$url = 'https://lokeshdhakar.com/projects/lightbox2/images/image-5.jpg';
$file = './files/'.basename($url);
file_put_contents($file, file_get_contents($url));
?>
For successful download, files directory must be exists. But I think it would be inefficient to add directory existence check as I think you already know where to save the file you are downloading.
The following code is mine.
I want to download a file and echo together in Php. But echo in end of code doesn't work, how to fixed this code?
$filename = trim($conf['savedir']).$_GET['task'].".".$_GET['hash'].".xml";
$downname = $_GET['task'].'.'.$_GET['hash'].'.xml';
$fp = fopen($filename, 'r');
if($fp == null)
{
echo "Wrong Access1";
} else {
header('Content-type:text/xml charset=utf-8');
header('Content-disposition: attachment; filename='.$downname);
header('Content-length:'.filesize($filename));
header('Content-transfer-encoding: binary');
header('Pragma: no-cache');
header('Expires: 0');
fpassthru($filename);
fclose($fp);
echo $_GET['task'].'.'.$_GET['hash'].'.xml file download.';
}
Like #Mark Backer said, you can't echo and download at the same time for the same request, but there are some "tricks" to echo something and download the file.
Let's suppose that your code from the question is located into test.php file and the user requests the file that contains the code bellow:
<script type="text/javascript">
window.open('test.php?task=<?=$_GET['task']?>&hash=<?=$_GET['hash']?>', '_blank');
</script>
<?php
echo $_GET['task'].'.'.$_GET['hash'].'.xml file download.';
So, the javascript code will pass the $_GET variables to test.php open a new window for download and in echo your message in the current window.
I am trying to read the file from php and downloading it from the ui on click of a button
<?php
$file = 'download.csv'; //path to the file on disk
if (file_exists($file)) {
header('Content-Type: application/csv');
header('Content-Disposition: attachment; filename=contact-list.csv');
header('Pragma: no-cache');
readfile($file);
//unlink($file);
exit;
}
else {
print "file not found!!!";
}
?>
But the file is not getting downloaded. No error, the code is working. unlink() function deleted the file.
Assuming my comment answers yes to all, then the only think i see that might be an issue.
application/csv
Should be:
text/csv
I am using php script to provide download from my website after a requisite javascript timer this php script is included which causes the download. But the downloaded file is corrupt no matter whatever I try. Can anyone help me point out where am I going wrong.
This is my code
<?php
include "db.php";
$id = htmlspecialchars($_GET['id']);
$error = false;
$conn = mysql_connect(DB_HOST,DB_USER,DB_PASSWORD);
if(!($conn)) echo "Failed To Connect To The Database!";
else{
if(mysql_select_db(DB_NAME,$conn)){
$qry = "SELECT Link FROM downloads WHERE ID=$id";
try{
$result = mysql_query($qry);
if(mysql_num_rows($result)==1){
while($rows = mysql_fetch_array($result)){
$f=$rows['Link'];
}
//pathinfo returns an array of information
$path = pathinfo($f);
//basename say the filename+extension
$n = $path['basename'];
//NOW comes the action, this statement would say that WHATEVER output given by the script is given in form of an octet-stream, or else to make it easy an application or downloadable
header('Content-type: application/octet-stream');
header('Content-Length: ' . filesize($f));
//This would be the one to rename the file
header('Content-Disposition: attachment; filename='.$n.'');
//Finally it reads the file and prepare the output
readfile($f);
exit();
}else $error = true;
}catch(Exception $e){
$error = true;
}
if($error)
{
header("Status: 404 Not Found");
}
}
}
?>
This helped me in case of more output buffers was opened.
//NOW comes the action, this statement would say that WHATEVER output given by the script is given in form of an octet-stream, or else to make it easy an application or downloadable
header('Content-type: application/octet-stream');
header('Content-Length: ' . filesize($f));
//This would be the one to rename the file
header('Content-Disposition: attachment; filename='.$n.'');
//clean all levels of output buffering
while (ob_get_level()) {
ob_end_clean();
}
readfile($f);
exit();
First of all, as some people pointed out on the comments, remove all spaces before the opening PHP tag (<?php) on the first line and that should do the trick (unless this file is included or required by some other file).
When you print anything on the screen, even a single space, your server will send the headers along with the content to be printed (in the case, your blank spaces). To prevent this from happening, you can:
a) not print anything before you're done writing the headers;
b) run an ob_start() as the first thing in your script, write stuff, edit your headers and then ob_flush() and ob_clean() whenever you want your content to be sent to the user's browser.
In b), even if you successfully write your headers without getting an error, the spaces will corrupt your binary file. You should only be writing your binary content, not a few spaces with the binary content.
The ob_ prefix stands for Output Buffer. When calling ob_start(), you tell your application that everything you output (echo, printf, etc) should be held in memory until you explicitly tell it to 'go' (ob_flush()) to the client. That way, you hold the output along with the headers, and when you are done writing them, they will be sent just fine along with the content.
ob_start();//add this to the beginning of your code
if (file_exists($filepath) && is_readable($filepath) ) {
header('Content-Description: File Transfer');
header("Content-Type: application/octet-stream");
header("Content-Disposition: attachment; filename=$files");
header('Expires: 0');
header('Cache-Control: must-revalidate, post-check=0, pre-check=0');
header('Pragma: public');
header("content-length=".filesize($filepath));
header("Content-Transfer-Encoding: binary");
/*add while (ob_get_level()) {
ob_end_clean();
} before readfile()*/
while (ob_get_level()) {
ob_end_clean();
}
flush();
readfile($filepath);