We help IT Professionals succeed at work.

How to serve a rackspace file, but force "Save As..."?

DrDamnit
DrDamnit asked
on
237 Views
Last Modified: 2014-08-25
I have some files living at Rackspace. To avoid filename collision, they are all renamed to an md5 hash of microtime(true), and the original file name is saved in the database.

When a user is to download one of these files, I need to:
1. serve the file straight from rackspace (they are huge).
2. Have the file pre-renamed back to the original file name so that when the user downloads it, it is transparent.

I have tried this:
			/* Extract information from Rackspace */

			$headers=get_headers($file_path);
			$buffer = explode(":", $headers[4]);
			$content_length = trim($buffer[1]);
			$buffer = explode(":", $buffer[7]);
			$content_type = trim($buffer[1]);

			$resource = fopen($file_path,'rb');

			/* Resend to user */
			http_send_content_disposition($filename,true);
			http_send_content_type($content_type);
			// http_send_file($file_path);
			http_send_stream($resource);

Open in new window


but it comes back with "File not found". However, when I do header("Location: $file_path") I get the files perfectly. (They are just named the hash, which is not nice ot the user).

What am I missing here?
Comment
Watch Question

Most Valuable Expert 2011
Author of the Year 2014

Commented:
Dr. Dammit: I think you can force a file download with something like this script (at least it works for me).  Add your own logic at line 30 to create the file name you want the client to see.

<?php // demo/force_download.php
error_reporting(E_ALL);
ini_set('display_errors', TRUE);
ini_set('log_errors',     TRUE);


// DEMONSTRATE HOW TO CAUSE A FILE DOWNLOAD


// REQUIRED FOR USE WITH THE PHP date() FUNCTIONS
date_default_timezone_set('America/New_York');

// A FILE TO DOWNLOAD - THIS LINK COULD COME IN THE URL VIA $_GET, OR COULD BE GENERATED INSIDE THE SCRIPT
$url = "http://www.IcoNoun.com/demo/short_text_file.txt";

// THE USE CASE FOR THE FUNCTION
force_download($url);


// FUNCTION TO FORCE A DOWNLOAD FROM A FILE
function force_download($filename)
{
    // GET THE CONTENTS OF THE FILE
    $filedata = @file_get_contents($filename);

    // SUCCESS
    if ($filedata)
    {
        // GET A NAME FOR THE FILE
        $basename = basename($filename);

        // THESE HEADERS ARE USED ON ALL BROWSERS
        header("Content-Type: application-x/force-download");
        header("Content-Disposition: attachment; filename=$basename");
        header("Content-length: ".(string)(strlen($filedata)));
        header("Expires: ".gmdate("D, d M Y H:i:s", mktime(date("H")+2, date("i"), date("s"), date("m"), date("d"), date("Y")))." GMT");
        header("Last-Modified: ".gmdate("D, d M Y H:i:s")." GMT");

        // THIS HEADER MUST BE OMITTED FOR IE 6+
        if (FALSE === strpos($_SERVER["HTTP_USER_AGENT"], 'MSIE '))
        {
            header("Cache-Control: no-cache, must-revalidate");
        }

        // THIS IS THE LAST HEADER
        header("Pragma: no-cache");

        // FLUSH THE HEADERS TO THE BROWSER
        flush();

        // WRITE THE FILE
        echo $filedata;
    }

    // ERROR
    else
    {
        trigger_error("ERROR: UNABLE TO OPEN $filename", E_USER_ERROR);
    }
}

Open in new window

CERTIFIED EXPERT
Most Valuable Expert 2012

Author

Commented:
Ray:

Here's the challenge: the file_get_contents works for files that are 8MB. I have some that are 800MB. that would cause a significant delay to transfer 800MB from Rackspace to the server, and then re-serve from there... that's what started the whole problem. The file sizes.
Most Valuable Expert 2011
Author of the Year 2014

Commented:
Can you please give me the URL of one of the files?  I'll try some ideas.
CERTIFIED EXPERT
Most Valuable Expert 2012

Author

Commented:
little bit of progress. This is still a relay, but serves (at least) the first 15K of the file:
<?php

$file_path = 'https://fe5c6f1e4c8b84451ab6-b2d3efd6307726e0e9944246b7b38ca9.ssl.cf1.rackcdn.com/16079_293db4eed8f033b3cb3823421dbe2408.jpg';

			/* Extract information from Rackspace */

			$headers=get_headers($file_path);
			// die("<pre>".print_r($headers,true)."</pre>");
			$buffer = explode(":", $headers[4]);
			$content_length = trim($buffer[1]);
			$buffer = explode(":", $headers[7]);
			$content_type = trim($buffer[1]);

			$resource = fopen($file_path,'rb');

			$filename = "test-image.jpg";
			/* Resend to user */
			http_send_content_disposition($filename,false);
			http_send_content_type($content_type);
			// http_send_file($file_path);
			while($data = fread($resource,1024*1024))
			{
				http_send_data($data);
			}

			fclose($resource);
?>

Open in new window

CERTIFIED EXPERT
Expert of the Year 2014
Top Expert 2014

Commented:
Wouldn't this be easier in javascript
Pity the download attribute isn't supported in IE else that would be the easiest route.
CERTIFIED EXPERT
Most Valuable Expert 2012

Author

Commented:
I am open to doing it in Javascript. What are your thoughts Gary?
CERTIFIED EXPERT
Most Valuable Expert 2012

Author

Commented:
Oh, and Gary, remember... the filename itself has to come from the DB. Remember: original name "MyPicture.jpg". Rackspace name: asldkfjpao8sdjfopi.jpg.

User cave man say: "When me download, me need pretty name!"
CERTIFIED EXPERT
Expert of the Year 2014
Top Expert 2014

Commented:
Will have a little think...
Can you place any restrictions on the browser used?
If so (FF,Chrome,Opera) you could just use:

<a href="sdfswerweh23423847.zip" download="myfile.zip"></a>
CERTIFIED EXPERT
Most Valuable Expert 2012

Author

Commented:
Some users have WinXP (and *shudder* old versions of IE). Although, Chrome, Firefox, and Safari (newest versions) are the officially supported browsers. And I guess the new IE.
CERTIFIED EXPERT
Expert of the Year 2014
Top Expert 2014

Commented:
Ermm how about
https://github.com/dcneiner/Downloadify

I would assume this is not gonna happen on tablets/mobiles?
CERTIFIED EXPERT
Most Valuable Expert 2012

Author

Commented:
My preference is to get this done using HTTP Headers. It seems so (ridiculously) simple to tell the browser: "Go get this, and here's the name for it"... there has to be something simple I am (we are) missing.
CERTIFIED EXPERT
Most Valuable Expert 2012

Author

Commented:
Downloadify seems like a good solution, however, I am fixing an existing codebase, and to add that would be quite an undertaking considering the site is live.
Most Valuable Expert 2011
Author of the Year 2014
Commented:
This one is on us!
(Get your first solution completely free - no credit card required)
UNLOCK SOLUTION
CERTIFIED EXPERT
Expert of the Year 2014
Top Expert 2014

Commented:
PHP is not going to be able to do this without downloading the file itself which negates the reason for an external server

I cannot find a way to do this in JS that will work cross browser, the only real alternative (if you cannot use the flash alternative as above) is to use the new HTML5 download attribute and try to limit downloading to the aforementioned browsers.
CERTIFIED EXPERT
Most Valuable Expert 2012

Author

Commented:
Excellent. Drop in replacement for my problem. Thanks, Ray.

@GaryC123: I think that your solution is also excellent, and I may revisit it int he future. If I could use Javascript to force the save as dialog without having to change our existing page code too much, and still stream from Rackspace, that would have been ideal.
CERTIFIED EXPERT
Expert of the Year 2014
Top Expert 2014

Commented:
I thought you wanted to serve the file directly and not be downloaded by your server first
Most Valuable Expert 2011
Author of the Year 2014

Commented:
I don't think the script is downloading the file to the server where the PHP runs.  I think it's just passing it through to the output buffers, apparently in chunks.  When you add memory_get_peak_usage(TRUE) the PHP script only used 2,621,440 bytes for a 27Mb file.  And interestingly, it downloaded the file to my client machine in about 10 seconds, which was a fraction of the time it took me to upload the same file to my server.
CERTIFIED EXPERT
Expert of the Year 2014
Top Expert 2014

Commented:
Yes you're right
Unlock the solution to this question.
Join our community and discover your potential

Experts Exchange is the only place where you can interact directly with leading experts in the technology field. Become a member today and access the collective knowledge of thousands of technology experts.

*This site is protected by reCAPTCHA and the Google Privacy Policy and Terms of Service apply.

OR

Please enter a first name

Please enter a last name

8+ characters (letters, numbers, and a symbol)

By clicking, you agree to the Terms of Use and Privacy Policy.