Attach files in Rails and read them in ExtJs + CakePHP - php

I have a project that is composed of two separate parts. On one side, there is a Rails app and on the other side, there is an ExtJs client with CakePhp server.
What needs to happen is this: attach a file in Rails (this is done by using Paperclip) and be able to read them on the Cake side. It might be easy if both systems were on the same server, but this needs to be done remotely, so the Cake side will call a Rails route and Rails will provide the file.
Cake code to download file:
function download_file($path, $file_name, $content_type, $size) {
if ($fd = fopen ($path, "r")) {
header("Content-type: " . $content_type);
header("Content-Disposition: attachment; filename=\"".$file_name."\"");
header("Content-length: $size");
header("Cache-control: private"); //use this to open files directly
header('Pragma: public');
ob_clean();
flush();
echo readfile($path);
// $this->log(apache_response_headers(), 'debug');
}
fclose ($fd);
}
Ruby / Rails code to provide document:
send_file document.path, :type => document.document_content_type
Rails Document model:
class Document < ActiveRecord::Base
belongs_to :attachable, :polymorphic => true
has_attached_file :document,
:url => '/download/:id/:fingerprint/documents'
def path
document.path
end
end
Everything works as expected except one type of file, .docx. I can upload and after that download any other files except those of this type. I can read the file in rails, after it was uploaded, with no problem. There seems to be something wrong on the Cake side, as what happends there is this: I can download the file, but I can't open it with LibreOffice after that. I seems that the mime types are set up correctly as apache and the browser recognize the type of file and the application to open it. It seems I can open the file with TextEdit.
So, the question is: what is wrong? Why don't .docx files open on the php / Cakephp side?
Any ideas will be highly appreciated.

Found the answer for this. Actually it was some sort of Cake issue after all, and it was related to the way Cake is rendering views.
Cakes MediaView doesn't allow working with remote files because of the way it builds the path of the file, but I've added
$this->autoRender = false;
and everything went well after that.
This post was helpful: Corrupted .docx download using phpdocx
Thanks to burzum as well for the hint with MediaView.

Related

Readfile is best solution to download external files?

I need to get a remote file and give it to user without saving it to my server disk (for hiding original URL) and found a lot of posts about download external files with various functions like file_get_contents or readfile. Already I'm using this one:
function startDownload($url){
if($this->url_exists($url))
{
//get filename from url
$name=$this->getFileName($url);
//first flush clear almost output
ob_end_flush();
//final clear
ob_clean();
//set headers
header('Content-Type: application/octet-stream');
header("Content-Transfer-Encoding: Binary");
header("Content-disposition: attachment; filename=\"" . $name . "\"");
//send file to client;
readfile($url);
//exit command is important
exit;
}
else JFactory::getApplication()->enqueueMessage(JText::_('URL_NOT_FOUND'), 'error');
}
And that's working but there is a problem! For a file with 200 MB size it takes ~ 10 seconds to start download in client browser. I think it's because readfile first downloads whole file to my server buffer and then give it to user. Is that right?
And is it possible to make it faster? for example download be started before fetch ended or it isn't possible technically?
In fact I don't know that this method is optimised or not. Any technical advice would be appreciated.
Note :
I know that this function should be changed for big files and that's not my concern now.
I consider to buy the external server in the same datacenter to make this download faster.
Target is that [File server] be separate than the file [online shop].
I tested curl method that mentioned by #LawrenceCherone. It worked nicely but when moved it to my project the result was the same as readfile (white screen for a few seconds).
So suspect to readfile() function. Separate my previous code to a single PHP file and result was amazing! Download starts immediately.
So I think my guess wasn't right and problem was not related to readfile function.
After a little search found a minor modification. I added below line :
while (ob_get_level()) ob_end_clean();
before the :
readfile($url);
And now download starts before whole file fetched in my server.

How to download a file without an extension in PHP

I have a file with no extension on it, but I know it's a tiff. I want to be able to download this file via PHP.
I created a page with a link to another php page, which has the following content:
<?php
$imgPath = 'http://server/23700-b074137f-eb5c-45d6-87c2-13c96812345b';
header("Content-disposition: attachment; filename=invoice.tiff");
header("Content-type: image/tiff");
readfile($imgPath);
?>
When I click the link, I get a prompt to download invoice.tiff, but it's 0 bytes.
However, if I rename the file on the server to 23700-b074137f-eb5c-45d6-87c2-13c96812345b.tiff (and change the $imgPath), it works.
How do I accomplish this without renaming the file to include the extension?
It's possible the 'tiff' extension is registered as a known file type on the server, so when you rename and request the tiff it's permissions will allow you to open it. However, with no extension, the security is probably stopping you from reading it, as mentioned by 'Mike B' above. To check this try just entering the file name in your browser address bar and see if it opens, both with and without the 'tiff' extension. There is no workaround for getting past the security issue, short of changing the severs security which would be very bad.
You are retrieving the file from a URL, therefore activating the 'fopen wrappers' in readfile. In general, you should not do this, especially when working locally since it invokes a lot of unnecessary overhead and (in this case) unwanted 'magic' behaviour.
Just use readfile on the local path to the file, and it'll be fine, or use die(file_get_contents($imgPath)) instead of the last line to circumvent PHP's native behaviour.
It works for me:
$imgPath = 'http://server/23700-b074137f-eb5c-45d6-87c2-13c96812345b';
$f = fopen($imgPath, "r");
header("Content-disposition: attachment; filename=invoice.tiff");
header("Content-type: image/tiff");
fpassthru($f);
You should also add the content-length header like so:
// untested code
header('Content-Length: '.strlen(stream_get_contents($imgPath)));

Creating simple button for .mp3 file download (no right-click, no media player)

I'm sure this is a simple task, but on my wordpress site I want to create a download button that forces an .mp3 download, without opening a player (when left clicked), or the user having to right-click 'save target as'. I just need a straight forward button, that when left-clicked causes a file to be downloaded (as well as being easily trackable by Google Analytics).
Is a .php script required for this? You'd think this would be a very common function, and easy to solve....but I have spent hours on this and have been unable to get anything to work.
*if it's not obvious my coding skills are nearly non-existent.
I really appreciate anybody's time who can help me figure this out. Thanks!
***EDIT
Just found this on another post, but no comments if it would work or not. It was for a .pdf file though...
<?php
if (isset($_GET['file'])) {
$file = $_GET['file'] ;
if (file_exists($file) && is_readable($file) && preg_match('/\.pdf$/',$file)) {
header('Content-type: application/pdf');
header("Content-Disposition: attachment; filename=\"$file\"");
readfile($file);
}
} else {
header("HTTP/1.0 404 Not Found");
echo "<h1>Error 404: File Not Found: <br /><em>$file</em></h1>";
}
?>
Save the above as download.php
Save this little snippet as a PHP file somewhere on your server and you can use it to make a file download in the browser, rather than display directly. If you want to serve files other than PDF, remove or edit line 5.
You can use it like so:
Add the following link to your HTML file.
Download the cool PDF.
Well, this is possible, but you need to write a script to do it. This is a pretty poor (security and basic coding wise) from http://youngdigitalgroup.com.au/tutorial-force-download-mp3-file-streaming/
file: downloadit.php
<?php
header ("Content-type: octet/stream");
header ("Content-disposition: attachment; filename=".$file.";");
header ("Content-Length: ".filesize($file));
readfile($file);
exit;
?>
you would then place it into a publicly accessible folder and build your links as such:
http://www.yoursite.com/downloadit.php?file=/uploads/dir/file.mp3
what this does is tells the browser to treat the file as a stream of bytes, rather than a particular MIME type which the browser would ordinarily do based on the file extension.

Link to a PDF in html, the file has no extension, but I know it is pdf how to make it open appropriately

First post. I'm working on a project for a client where they have pdf files uploaded to a file structure (LAMP Stack) but the files have no extensions on them. Under the assumption that those files have to be PDF how would I get the browsers to understand that, and open them accordingly? Obviously with adding the file extensions this would suddenly work but I can't change the way their system works, it would result in too many changes and they are on a tight deadline. As for saving a temporary copy somewhere, I could do that, but I was hoping for a better solution. Is there a way to suggest to the browsers that they open a file a certain way?
Any thoughts guys/gals?
You just set the application type and file name in the headers, like so:
// This points to the file in question, note that it doesn't
// care whether it has an extension on the name or not.
$filePathOnDisk = '/path/to/your/pdffile';
// You can make this whatever you like, it doesn't have to
// be the same as the file name on the disk! This is the name of the file your end
// user will see when they are asked if they want to save. open, etc in the browser.
$fileName = 'file.pdf';
$data = file_get_contents($filePathOnDisk);
header("Content-type: application/pdf");
header("Content-disposition: attachment;filename=$fileName");
echo $data;
See PHP: stream remote pdf to client browser and Proper MIME media type for PDF files for reference as well.
Tested
You can use the following which will prompt the user to save the (PDF) file on their computer.
Notice the different file names.
One is the file that will be uploaded/prompted to the user download_example.pdf, while the other is the file without an extension as set in readfile('example');
<?php
header('Content-type: application/pdf');
header('Content-Disposition: attachment; filename="download_example.pdf"');
readfile('example');
?>

Cakephp video passthrough

I have been developing a website for teachers using cakephp 2.0.3. One requirement is for the teachers to be able to upload files (images, docs, pdfs and videos) that are only viewable by a students in a given class. I had been using a php fpassthrough to read binary data to the http output stream.
This has worked fine for all the documents except for the video, which won't be played by the chrome built-in player, vlc firefox plugin or jwplayer, which will be used on the actual web site. If I save the file, however, Windows media player will play the file fine. Playing the file from the cakephp webroot directory also works fine. Here is the file view code:
The controller code, which loads and outputs the file:
//cakephp find used to pull file information from database
$fileName = "/redacted/" . $File['File']['unit_id'] . "/" . $File['File']['id'];
$fp = fopen($fileName, 'rb');
header("Content-Type: " . $File['File']['type']);
header("Content-Length: " . filesize($fileName));
fpassthru($fp);
The layout which is used:
<?php echo $this->fetch('content'); ?>
The jwplayer loading code:
<div style="width: 200px; height: 91px;" id="1"> </div>
<script type="text/javascript">
jwplayer("1").setup({
file: "/Files/view/1/blah.m4v",
});
</script>
The view file is empty, as all output is done in the controller. I've looked through this site and google but can't find a reason for the issue or better way to perform this function. I have verified that the headers are correct and that there are no errant line breaks or data before or after. I have also checked that the extension is not required (in the final code block, '1' specifies the file ID, and the file name at the end is a dummy).
My questions, therefor:
Is fpassthru(), readfile() or similar the correct method for php video passthrough?
Is this problem specific to my code, or inherent to cakephp's handling of MVC? Has anyone successfully implemented such a solution?
Are there any other 'thinking outside the box' solutions (using node.js or some other language, .htaccess trickery) that would work better.
I could use a non-cakephp script in the webroot folder, but how would I go about passing user session data? Is this just a dumb idea?
[Edit]
After testing fpassthrough on video files separate from cakephp, I found that the issue is with the video passthrough rather than cakephp. This code was used to test, in the app/webroot directory:
<?php
$fileName = "/var/www/redacted/files/2/1";
$fp = fopen($fileName, 'rb');
header("Content-Type: video/mp4");
header("Content-Length: 18485");
fpassthru($fp);
?>
The content type, path, and length were taken from the database entry for that file.
Cake has the functionality you want built in, check out Media Views in the documentation. Note that it's deprecated in Cake 2.3.0, but since you're using 2.0, it should work just fine. I tested the following code and had no problem with it playing a sample m4v in jwplayer or the VLC plugin:
$this->viewClass = 'Media';
$fileName = WWW_ROOT . DS . 'sample_iPod.m4v';
$parts = pathinfo($fileName);
$params = array(
'id' => $parts['basename'],
'name' => $parts['filename'], // requires PHP >= 5.2.0
'extension' => $parts['extension'],
'download' => false,
'path' => $parts['dirname'] . DS
);
$this->set($params);
Note that Cake will automatically output the correct content type/length for you based on the extension.

Categories