My website contains a section that allows users to access restricted PDF files if they have access to them. Basically, they arrive at a page which has a nav bar at the top, allowing them to cycle between PDF files. Below this, there is an iframe which is linked to a PHP page that does the retrieval of the file, and displays it. This file is called falr_pdf.php, and depending on how it is accessed, the PDF file is either displayed inline, or downloaded as an attachment.
Here is the navbar page, along with the falr_pdf.php IFRAME embedded, to give you a clear idea.
As you can see, the PDF should be displayed inline, and then if the user clicks "Download File", the same page is opened in a new tab, but to download the file as an attachment instead of displaying it inline.
My problem is that this works flawlessly for small PDF files, but anything larger than about 1.6MB will take an incredibly long time to display inline. However, if they are downloaded using the direct link instead, they download at normal speed, very quickly. Here is the code I am using...
$path = $falr_filesbasedir . "/" . $folder . "_pdf/" . $file . ".pdf";
$public_name = basename($path);
header('Content-Length:'.filesize($path));
header('Content-type: application/pdf');
header('Content-Disposition: ' . $method . '; filename="' . $public_name . '"');
readfile($path);
exit;
Now I know that the issue is not in any of my variables or links, since it works fine for every type of small file, and the download works fine for large files. It's only that large files don't display inline correctly.
$method is only ever "inline" or "attachment"
Is this a bug with readfile()? Or is it something to do with my PHP settings?
I am at a complete loss here.
Have you tried looking at your performance waterfall? Have you tried accessing the file served directly from the webserver as a static document? Have you tried measuring timings in your php code and comparing that with what's happening in the browser? What is the latency and bandwidth between client and server? Whatt happens when you download the file using a client running on the same host as the server? Are there any signs of stress on the server host? These are the basic questions you should start by investigating.
At a guess, the most likely cause is buffering in php or on the webserver. If you unroll the readfile into read/write operations with flushes ever 200kb you can test this. But you need to start measuring properly.
Related
Background
I am working on setting up an intranet webpage where the user can select an item from a list, and it will display information about that item and a PDF as a block element.
The PDFs will need to be updated and replaced by specific people, so they are stored on a separate file server on the internal network.
HTML Solution Attempt
I've tried using
<embed src="file:///myFileServer/PDFs/filename.pdf" width="1000" height="600" type="application/pdf">
but I get local resource errors:
Firefox: "Security Error: Content at myWebServer/file_read.php may not load or link to file:///myFileServer/PDFs/filename.pdf."
Edge/Chrome: "Not allowed to load local resource: file:///myFileServer/PDFs/filename.pdf"
I understand the error is because the PDF path is loaded by the browser, not the web server. This likely would be a problem even if it was allowed, because not all of the users would have access to the file server. Naturally, my next thought was to try using PHP to display the element so that the browser never needs to see a file path and the web server loads the PDF.
PHP Solution Attempt
I then tried using
<?php
$filename = "file:///myFileServer/PDFs/filename.pdf";
header("Content-type: application/pdf");
header("Content-Length: " . filesize($filename));
readfile($filename);
?>
This gave me the following warning, which had to do with the PHP that is used to load the page's navigation bar.
"Cannot modify header information - headers already sent by (output started at C:\Apache24\htdocs\nav.php:66) in C:\Apache24\htdocs\file_read.php on line 81"
Removing <?php include 'nav.php' ?> in the file_read.php caused the PHP code mentioned above to work, but not as I wanted. It basically loads a full-page PDF view as if you selected a PDF to open it in the browser, instead of displaying it as a page element.
PHP PDF to Base64 Solution
Since the above did not work, I kept digging for a solution. It's a little hacky, but I did find one solution that seems to works.
<?php
$fileLocation = "//myFileServer/PDFs/filename.pdf";
$pdf = chunk_split(base64_encode(file_get_contents($fileLocation))); //Convert to Base64
echo '<embed src="data:application/pdf;base64,' . $pdf . '" style="height:500px;width:80%;" title="test"></embed>';
?>
While this does work and the code is rather clean, it's a bit slow and I feel like there has to be a more efficient way to do this.
My Question
Is there a better way to deal with the limitations of loading a local resource from another internal server than converting the PDF to Base64?
I'm outputting a PDF via PHP with the following code. $file is an object that contains data pertaining to the file being displayed.
header('Content-type: application/pdf');
header('Content-disposition: inline; filename="'.$file->name.'"');
#readfile($file->ServerPath());
My issue is that when I go to download the file in Chrome it will occasionally try to save the page instead of the PDF.
For example, say the URL that is displaying the file is mywebsite.com/file?file_id=1234. Most of the time it will try to save the file correctly as "file_name.pdf". However, sometimes chrome will try to save the file as "file" with no extension. This seems to happen randomly.
If it makes any difference the page displaying the file is being opened in a new tab. The issue happens regardless of whether I redirect via PHP or Javascript.
I really need to resolve this issue, as these PDFs will be accessible by users.
Thanks in advance.
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.
My site uses bookmarklets to gather data from external sites, kinda like Pinterest. I'm concerned about security and want to move the images the bookmarklet gathers from the doc root up one level. My script has some hefty security checks in place, but I want to add this as a last line of defense.
How do I access my images within my script? Obviously using ../userimages/id/image.jpg wont work. I'm using Apache.
Thanks!
Proxy the image
You would use a proxy script to feed the images through like the following example:
// open the file in a binary mode
$name = '../userimages/id/image.jpg';
$fp = fopen($name, 'rb');
// send the right headers
header("Content-Type: image/png");
header("Content-Length: " . filesize($name));
// you may like to set some cache headers here
// dump the picture and stop the script
fpassthru($fp);
exit;
This example is from the PHP manuals fpassthru() page. You would save this script somewhere in your servers document root/httpdocs folder.
"Spoofing" the URL to the image
The easiest way to give the PHP file the appearance of being an image file to a user/browser is to use Apaches mod_rewrite. Usually I use a URL structure something like this:
http://www.example.org/image-id/image.png
Where image-id is the unique identifier for that particular image. This way the file has the correct extensions of an image instead of .php.
My intention is to convert a total php page into html, and subsequently convert the html to pdf and render it through the browser.Which is done , apart from that while showing it on the browser , it will simultaneously download the pdf automatically which is not happening.
Its with PHP.
Can to tell me the basic concept ..as to how to do this.
Thanks in advance
You already render the page in the browser. Before displaying the page, header() the user to the location which will serve the same page as an attachment, but do not exit. This will allow them to download the file, but it will still load the file on the page. Not 100% sure that this will work, but it's worth a shot.
BTW different browsers will handle pdfs differently and depending on settings, plugins, etc. For instance, some might try to download the file anyway instead of showing it in the browser.
I think you need to look over the question again. As I read it, you're asking how to display something that hasn't yet been downloaded (while it's downloading), and that is obviously not possible and so cannot be what you mean.
Try creating a header, which tells the browser what to do when it receives the file.
<?php
header("Content-Type: $filedatatype" );
header("Content-Disposition: attachment; filename=\"" . $FileObject->name . "\";");
header("Content-Transfer-Encoding: binary");
header("Content-Length: " . $filesize);
?>