HTML - In browser viewing and forced downloads - php

On the website I am working on I like to offer users two links for every download, "View" and "Download". Most if not all of these downloads will be PDF format. The goal of View is to cause the document to be displayed in the browser with the PDF plugin/extension, whereas the Download link forces a download to local storage.
I have it working this way:
<div class="DownloadLinks">
View
Download</div>
</div>
But now I want to do it this way (using PHP to store and retrieve documents) and it doesn't work any more:
<div class="DownloadLinks">
<div class="DownloadLinks">View
Download</div>
</div>
I think the problem might be that the browser doesn't see a file extension in the link therefore performs a download instead of firing up the PDF plugin. Unfortunately I am just using a Content Management System and I don't have access to the PHP script and I can't change any of that. How can I get this to work as intended?

I would recommend just using javascript because of your CMS. Here's a good function for that.
var downloadURL = function downloadURL(url) {
var hiddenIFrameID = 'hiddenDownloader', iframe = document.getElementById(hiddenIFrameID);
if (iframe === null) {
iframe = document.createElement('iframe');
iframe.id = hiddenIFrameID;
iframe.style.display = 'none';
document.body.appendChild(iframe);
}
iframe.src = url;
};
This would be for the download script. If you wanted to uses php so that you could also track the number of downloads (or something along thos lines), your could just use AJAX for that.

You need to add the following headers to get_document.php when you click the download link (you may need an extra GET parameter to indicate this):
header('Content-Type: application/octet-stream');
header('Content-Disposition: attachment; filename="'.basename($file).'"');
header('Content-Length: ' . filesize($file));
Where $file is a path to your file, alternatively you can manually substitute the name and file size, but make sure the name includes the correct extension.
Edit, you say you don't have access to the CMS - how about the HTTP server? Or is it a hosted service?

Related

How to create a video stream from a single dynamic image in PHP

I have a single image file on my server which its content changes every 100 ms.
I can load and serve this image as usual:
$image = file_get_contents('path/to/image.jpg');
header('Content-type: image/jpeg');
echo $image;
<img src="image.php">
Thus, every time the user updates the screen (pressing F5 for example) the server would reply a different image. I can also use Java Script (using setInterval for example) to update the image continuously so the users needn't to update the screen themselves.
However, I need to serve this image as a CONTINUOUS FLOW such as a LIVE VIDEO STREAM in order to be shown as an HTML5 video instead of a static image.
Some examples I`ve found so far use PHP-FFMpeg library for stream videos. It turns out that those examples require that I have a video file at hand (a file in the OS or a URL to a file) instead of a single (dynamic) image as I've described above.
I found this example for how to use PHP to streaming. It looks promisssing. But again the code supposes I have a video file url which I haven't.
I'm wondering if is it possible to adapt this code to my needs. For example, how to adapt the setHeader() method to the scenario where there are no begin and end? And considering that I have loaded the image contents using file_get_contents or so, how to change stream() properly? Or, at other hand, is there other way to serve this image as a video stream?
The code below should reload the image ever 200 ms. Adding a random number avoids any potential caching which is unlikely since you are requesting a PHP page.
<html>
<header>
<script>
function ReloadImage()
{
var image_element = document.getElementById('image_id');
image_element.src = 'image.php?rand=' + Math.random();
}
setInterval(ReloadImage,200);
</script>
</header>
<body>
<img src="image.php" id="image_id">
</body>
</html>
Well, I just found a solution for my needs:
$identifier = "an_identifier";
//set headers
header('Accept-Range: bytes');
header('Connection: close');
header('Content-Type: multipart/x-mixed-replace;boundary=' . $identifier);
header('Cache-Control: no-cache');
// loop to continuously serve an image
while(true) {
$image = load_image_contents();
echo "--" . $identifier . "\r\nContent-Type: image/jpeg\r\nContent-Length: ".strlen($image)."\r\n\r\n".$image;
flush();
usleep(50000);
}
On the browser side I just set a regular image tag:
<img src="image.php">

Open PDF at specific page using #page=n when serving file from PHP proxy

This seems like it should be simple. I have a set of files I have to store outside of the webroot and have an access script to call them. I also need to sometimes tell a PDF that must be called via this proxy script to open at a specific page. Releveant part of the script below:
header('Content-type: application/pdf');
header('Content-Length: ' . filesize($file));
header('Content-Disposition: attachment; filename="'.$file_name.'"');
readfile($file);
$file_name is just the basename() of the file, and $file is the path to the file, with #page=2, or #page=10, or whatever appended to it. If I remove the hashtag portion, the script works fine and the PDF opens with no errors. When the hashtag portion is there, all the programs tell me the PDF has been corrupted and can't be open.
I can't seem to find anything on here or Google as to what I need to do. Do I need to set an additional header to simulate the hash tag? Use exec() to call some command line code instead of using readfile()?
Any insight would be greatly appreciated.
You have to append the #page=2 (or whatever page you want to open) to the URL in the browser, not the filename in the proxy-script.
You currently try to open a file myFile.pdf#page=2 from the filesystem that does not exist as the filename is myFile.pdf
The feature to open a pdf-file on a specific page on the other hand is implemented in the browser or it's PDF-plugin. Therefore the information which page to open has to be given to the browser via the URL. So you should call your proxyscript like this: http://example.com/proxy.php?myFile.php#page=2
Update:
If you want to download the file and open it at a specific page every time the file is opened from the local file-system of the user, you will have to edit (or recreate) the PDF-File.

How to Force a file to download using PHP on mobile Browsers?

I want to write a php script to download some files(extensions - .apk, .dcm, .pdf, .zip etc...) on mobile browsers. I have written a php code to download those files and it is working fine on all the browsers(not mobile browsers). But I tried it using a HTC mobile and it is trying to open the file instead of downloading(like opening a web page).
How can I enable the download on mobile browsers?
Thank You
PS:
This is how I do it.
I use a jquery code to send some parameters to a php file and it will return the appropriate download file path.
jQuery code snippets is:
$.post('saveData.php',{ name: name.val(), email: email.val(), phone:phone.val(), address:address.val(), version:vers, swVersion:swvers, type:type },
function(data)
{
var links = data;
document.body.innerHTML += "<iframe src='" + links + "' style='display:hide;' ></iframe>";
});
"links" variable contains the download path return from the php file.
Iframe allow the download window to popup. But it does not work on the mobile browsers.
Try with the following headers:
header('Content-Type: application/force-download');
header('Content-Disposition: attachment; filename="filename"');
The idea is to set the content type to something that the browser doesn't know how to open - that way it will show the save dialogue. You can try it with the actual MIME type, it should work, but I can't test it right now.

How do I stop embedded sound clips from being downloaded to the browser cache before they are requested to be played?

I have been scratching my head with this one for a few days,
I am building a web app for a client associated with call tracking. One aspect of the system is to allow for short recorded voicemail messages to be played directly from a "play" button beside the message details.
I am accessing the sound files as such;
$wavefile=$calls_row['recording'];
if (file_exists("/var/spool/asterisk/monitor/".$calls_row['recording'].".wav")){
$wavefile="
<form>
<input type=\"button\" value=\"Play\" onClick=\"PlaySound('clip-".$stat_id."')\" />
</form>
<embed src=\"pass-thru.php?f=".$calls_row['recording'].".wav\" autostart=\"false\" width=\"0\" height=\"0\" id=\"clip-".$stat_id."\" enablejavascript=\"true\" />
";
}
The sound clips are stored outside the web root in a directory I have been told we cannot change. Being unable to get at the files locally I put together a small php pass-through script to pass me the file back to my web page.
The sound clip is using a small piece of javascript to get the ID of the link for the specified file before playing it;
function PlaySound(soundobj) {
var thissound=document.getElementById(soundobj);
thissound.Play();
}
The files are accessed outside the webroot and passed to the html by the following php code;
$basedir = '/var/spool/asterisk/monitor';
$clip = $_GET['f'];
$file = $basedir.'/'.$clip;
$ext = array_pop(explode ('.', $file));
if(file_exists($file) && $ext != '' && isset($allowed[strToLower($ext)])) {
$type = $allowed[strToLower($ext)];
}
header("Content-type: audio/x-wav");
header("Content-Disposition: filename='{$file}'");
header("Content-Transfer-Encoding: binary");
header('Content-Length: ' . filesize($file));
readfile($file);
exit();
Everything works fine as it should, the clips are passed through and the sound clips play in the browser.
The problem is that all of the soundclips are loaded into the cache when the page loads, obviously slowing things down a bit. I have the results paginated so its only downloading 25 at a time but I would really love to just have the links to the soundclips available and that they are only downloaded when selected to be played.
Any ideas would be appreciated.
The easiest option I can think of is not embedding the sound file in the HTML. As far as I know, the old-school embed element has no options to turn off preloading of sound files, for this feature you should look into HTML5's audio (which is not working in old IEs of course).
So basically, you just don't put an embed element into your HTML code, and when the Play button is clicked, you insert the embed element from Javascript. The data you need for the embed element to be created (URL for example) can be stored in a data- attribute, or in your case, simply written to the inline event handler's parameter list.
If you need to care about users with Javascript turned off, you can still include the embed element in a noscript tag (hopefully it won't start preloading).
For this method you only need some basic functions like document.createElement() and insertBefore().

Download CSV file using "AJAX"

I'm trying to accomplish a fairly simple task for my website, but I"m not sure exactly how to go about it. I want the user to be viewing a table, then click a button, at which point the user can save the contents of that table as a csv file. This request can sometimes be quite complicated so I generate a progress page to alert the user.
I have most things figured out except actually generating the csv file. (I use jQuery and PHP)
the jQuery code run on click:
hmis_query_csv_export: function(query_name) {
$.uiLock('<p>Query Loading.</p><img src="/images/loading.gif" />')
$.get({
url: '/php_scripts/utils/csv_export.php',
data: {query_name: query_name},
success: function(data) {
$.uiUnlock();
}
});}
the relevant PHP:
header("Content-type: text/x-csv");
header("Content-Disposition: attachment; filename=search_results.csv");
//
//Generate csv
//
echo $csvOutput
exit();
What this does is sends the text as the PHP file, but it's doesn't generate a download. What am I doing wrong?
If you are forcing a download, you can redirect the current page to the download link. Since the link will generate a download dialog, the current page (and its state) will be kept in place.
Basic approach:
$('a#query_name').click(function(){
$('#wait-animation').show();
document.location.href = '/php_scripts/utils/csv_export.php?query_name='+query_name;
$('#wait-animation').hide();
});
More complicated:
$('a#query_name').click(function(){
MyTimestamp = new Date().getTime(); // Meant to be global var
$('#wait-animation').show();
$.get('/php_scripts/utils/csv_export.php','timestamp='+MyTimestamp+'&query_name='query_name,function(){
document.location.href = '/php_scripts/utils/csv_export.php?timestamp='+MyTimestamp+'&query_name='+query_name;
$('#wait-animation').hide();
});
});
At PHP script:
#header("Last-Modified: " . #gmdate("D, d M Y H:i:s",$_GET['timestamp']) . " GMT");
#header("Content-type: text/x-csv");
// If the file is NOT requested via AJAX, force-download
if(!isset($_SERVER['HTTP_X_REQUESTED_WITH']) || strtolower($_SERVER['HTTP_X_REQUESTED_WITH']) != 'xmlhttprequest') {
header("Content-Disposition: attachment; filename=search_results.csv");
}
//
//Generate csv
//
echo $csvOutput
exit();
The URL for both requests must be the same to trick the browser not to start a new download at document.location.href, but to save the copy at the cache. I'm not totally sure about it, but seems pretty promising.
EDIT I just tried this with a 10MB file and it seems that val() is too slow to insert the data. Hurrumph.
Okay, so I gave this one another go. This may or may not be completely insane! The idea is to make an AJAX request to create the data, then use the callback to insert the data into a hidden form on the current page which has an action of a third "download" page; after the insertion, the form is automatically submitted, the download page sends headers and echoes the POST, and et voila, download.
All the while, on the original page you've got an indication that the file is being prepared, and when it finishes the indicator is updated.
NOTE: this test code isn't tested extensively, and has no real security checks (or any at all) put in place. I tested it with a 1.5MB CSV file I had laying about and it was reasonably snappy.
Index.html
<a id="downloadlink" href="#">Click Me</a>
<div id="wait"></div>
<form id="hiddenform" method="POST" action="download.php">
<input type="hidden" id="filedata" name="data" value="">
</form>
test.js
$(document).ready(function(){
$("#downloadlink").click(function(){ // click the link to download
lock(); // start indicator
$.get("create.php",function(filedata){ // AJAX call returns with CSV file data
$("#filedata").val(filedata); // insert into the hidden form
unlock(); // update indicator
$("#hiddenform").submit(); // submit the form data to the download page
});
});
function lock(){
$("#wait").text("Creating File...");
}
function unlock(){
$("#wait").text("Done");
}
});
create.php
<?php
//create $data
print $data;
?>
download.php
<?php
header("Pragma: public");
header("Expires: 0");
header("Cache-Control: must-revalidate, post-check=0, pre-check=0");
header("Content-Type: text/x-csv");
header("Content-Disposition: attachment;filename=\"search_results.csv\"");
if($_POST['data']){
print $_POST['data'];
}
?>
The best way to accomplish this is to use a Data URI as follows:
Make the AJAX call to the server as per normal
Generate the CSV on the server-side
Return the data (either bare or inside a JSON structure)
Create a Data URI in Javascript using the returned data
Set window.location.href to the Data URI
See this link for instructions (paragraph #3, specifically): http://en.wikipedia.org/wiki/Data_URI_scheme
This way, you don't need to save any files on the server, and you also don't need to use iframes or hidden form elements or any such hacks.
I don't think you can make the browser download using a AJAX/JS request. Try using a hidden iframe that navigates to the page which generates the CSV
Well the point of using AJAX is to avoid a visible reload of the page. If you want a download, you want the opposite,- a brand new request from the browser. I'd say, just create a simple button pointing to your php page.
To echo and expand on what others have said, you can't really send the file using AJAX. One of the reasons for this is (and someone correct me if I'm wrong on this, please) that the page you're currently on already has sent its content headers; you can't send them again to the same window, even with an AJAX request (which is what your PHP file is attempting to do).
What I've done before in projects is to simply provide a link (with target="_blank" or javascript redirect) to a separate download PHP page. If you're using Apache, check out mod_xsendfile as well.

Categories