We launched new forums in March 2019—join us there. In a hurry for help with your website? Get Help Now!
    • 36926
    • 701 Posts
    Hi I’m trying to set up a php page that will make the users browser prompt to save the image. I’ve managed to get this sort or working (thanks to Google), but i can only seem to get it to work if the path is a relative. If it’s absolute it doesn’t seem to work. I need to try and get this working with an absolute path as I plan on using friendly URLs on the site.

    This is what i have so far on a page called save-image.php:
    <?php
    $filename = "assets/galleries/13/IMG_1587.jpg";
    header("Pragma: public");
    header("Cache-Control: must-revalidate, post-check=0, pre-check=0");
    header("Cache-Control: private",false);
    header("Content-Disposition: attachment; filename=\"".basename($filename)."\";" );
    header("Content-Transfer-Encoding: binary");
    header("Content-Type: PHP Generated Data");
    header("Content-Length: ".filesize($filename));
    readfile($filename);
    ?>
    


    The above works fine but if I change the $filename value URL "/assets/galleries/13/IMG_1587.jpg" the it doesn’t seem to work.

    Anyone got ideas on what maybe wrong or maybe an alternative solution.

    Many Thanks

    Ben
      • 18913
      • 654 Posts
      This example

      http://apptools.com/phptools/force-download.php

      uses a line like this
      header("Content-Disposition: attachment; filename=\"$file\"");
      

      as opposed to your equivalent line
      header("Content-Disposition: attachment; filename=\"".basename($filename)."\";" );
      


      (you have an embedded semicolon)

      I only mention this because I have a similar routine which uses the first version of the code as opposed to yours and mine works. Alternatively, perhaps the building of the path is what is tripping up PHP?

      Anyway, just a stab ...
      Matt
        • 36926
        • 701 Posts
        Quote from: mconsidine at Jan 03, 2011, 04:29 PM

        This example

        http://apptools.com/phptools/force-download.php

        uses a line like this
        header("Content-Disposition: attachment; filename=\"$file\"");
        

        as opposed to your equivalent line
        header("Content-Disposition: attachment; filename=\"".basename($filename)."\";" );
        


        Thanks mconsidine, unfortunately that didn’t seem to change much. But thanks for the link, Id seen that link a while back but couldn’t seem to track it down again. So I’ll give this example a go.

        Thanks

        Ben

        (you have an embedded semicolon)

        I only mention this because I have a similar routine which uses the first version of the code as opposed to yours and mine works. Alternatively, perhaps the building of the path is what is tripping up PHP?

        Anyway, just a stab ...
        Matt

          • 36926
          • 701 Posts
          Hi Matt,

          Would you be able to provide an example of the code you have working as still having problems with this and the example link seems to require the files to be save in the same directory, which they won’t be in my case.

          Many Thanks

          Ben
            • 18373 ☆ A M B ☆
            • 3,141 Posts
            Why are you using basename(..)?

            I’ve never used that function before myself, but from the docs: http://php.net/manual/en/function.basename.php that seems to be cutting off the first part of the url, which obviously would be messing up the reference..
              Mark Hamstra • Developer spending his days working on Premium Extras and a MODX Site Dashboard with the ability to remotely upgrade MODX and extras to make the MODX world a little better.

              Tweet me @mark_hamstra, check my infrequent blog at markhamstra.com, my slightly more frequent ramblings at MODX.today or see code at Github.
              • 33983 ☆ A M B ☆
              • 181 Posts
              I looked at the code I use and this is a site that I have listed in my notes: http://w-shadow.com/blog/2007/08/12/how-to-force-file-download-with-php/comment-page-1/. It talks about turning off the zlib.output_compression.
              // like this:
              if ( ini_get('zlib.output_compression') ) {
                  ini_set('zlib.output_compression', 'Off');
              }
              


              I think you also need to define your content type correctly. Make sure that no other headers are going out first of that you have white space(extra lines before the php) in your file. This is the rest of the code I use:
              $file_type_array = array(
              	# documents
              	'doc' =>'application/msword',
              	'docx' =>'application/msword',
              	//'docx' => 'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
              	'rtf' => 'application/rtf',
              	'txt' => 'text/plain',
              	'pdf' => 'application/pdf',
              	# powerpoint
              	'pot' => 'application/mspowerpoint',
              	'pps' => 'application/mspowerpoint',
              	'ppt' => 'application/mspowerpoint',
              	'ppz' => 'application/mspowerpoint',
              	# excel
              	'csv' => 'application/x-msdownload',
              	'xlc' => 'application/vnd.ms-excel',
              	'xls' => 'application/vnd.ms-excel',
              	# web images
              	'gif' => 'image/gif',
              	'jpeg' => 'image/jpeg',
              	'jpg' => 'image/jpeg',
              	'png' => 'image/png', 
              	'tif' => 'image/tiff',
              	'tiff' => 'image/tiff',
              	# web files
              	'css' => 'text/css',
              	'htm' => 'text/html',
              	'html' => 'text/html',
              	'xml' => 'text/xml',
              	'js' => 'application/x-javascript',
              	# video
              	'avi' => 'video/x-msvideo',
              	'dl' => 'video/dl',
              	'fli' => 'video/fli',
              	'fli' => 'video/x-fli',
              	'flv' => 'video/flv',
              	'gl' => 'video/gl',
              	'mp2' => 'video/mpeg',
              	'mpe' => 'video/mpeg',
              	'mpeg' => 'video/mpeg',
              	'mpg' => 'video/mpeg',
              	'mov' => 'video/quicktime',
              	'qt' => 'video/quicktime',
              	'viv' => 'video/vnd.vivo', 
              	'vivo' => 'video/vnd.vivo', 
              	'wmv' => 'video/x-ms-wmv',
              	'wmx' => 'video/x-ms-wmx',
              	'wvx' => 'video/x-ms-wvx',
              	'asf' => 'video/x-ms-asf',
              	'asx' => 'video/x-ms-asx',
              	'movie' => 'video/x-sgi-movie'
              );
              $content_type = $file_type_array[$file_ext];
              $filesize = filesize($filename);
              if( is_file($filename) ){
                if ( $filesize ) {
              	header('Content-Type: ' . $content_type);
              	header('Content-Disposition: attachment; filename="'.str_replace(' ','_',$display_filename).'.'.$file_ext.'"');
              	header("Content-Transfer-Encoding: binary");
              	header('Accept-Ranges: bytes');
              	// The three lines below basically make the download non-cacheable
              	header("Cache-control: private");
              	header('Pragma: private');
              	$time = gmdate('D, d M Y H:i:s', filectime($filename));
              	header('Last-Modified: '.$time.' EST');
              	//header("Expires: ".$time." EST");
              	header('Expires: Mon, 26 Jul 1997 05:00:00 GMT'); // date in the past
              	header('Content-Length: '.$filesize);
                }
              
              /* Read It */
              $contents = fread(fopen($filename, "rb"), filesize($filename));
              /* Print It */
              echo $contents;
              }
              
                • 36926
                • 701 Posts
                Quote from: Mark at Jan 05, 2011, 02:15 PM

                Why are you using basename(..)?

                I’ve never used that function before myself, but from the docs: http://php.net/manual/en/function.basename.php that seems to be cutting off the first part of the url, which obviously would be messing up the reference..

                @Mark H, I’m only use basename on the file name, which is the name give to the file when saved. So you don’t want the full URL used in that.

                @Josh, Thanks I’ll give that example ago. I’ve read that you don’t need the content type correct which is how some examples work, they give it an unknow content type forcing the browser to download. But of course it better if I can and if your example allows this then thats a bonus.

                Thanks again

                Ben
                  • 36926
                  • 701 Posts
                  Ok some success.

                  I tried your example Josh, but was still having no luck with the absoulte path, working fine with relative. But your example gives an error which was stating it couldn’t find the file unlike mine which just download a empty file.

                  So this got me thinking and realised I was being a bit stupid with regards to the where the root was. It was actually the root of the server and not public directory.

                  So just need to make sure
                  /home/sites/domain.com/public_html/
                  was in the path.

                  So nearly there just need to play around so it uses a query string and can detect the correct image content type.

                  Thanks for the help all.

                  Ben