Forced Download of a file like a .vcf

8 posts by 3 authors in: Forums > CMS Builder
Last Post: April 16, 2013   (RSS)

I've seen a few examples of php and .htaccess 'auto' or 'forced' download codes but can't seem to integrate them into my pages - or else it wants to download the entire page  ( my ignorance in coding )

I want ( in this case ) the link to download the Contact Card, not display the code in the browser - ( or make the world change their browser settings )

So if I have this code:

<?php foreach ($attorneysRecord['vcard'] as $index => $upload): ?>
          <a href="<?php echo $upload['urlPath'] ?>"><img src="../images/vcard_logo.jpg" width="31" height="24" alt="Vcard" /></a>
        <?php endforeach ?>
       
and php header of

<?php header('Content-type: text/html; charset=utf-8'); ?> 

What do I need?

As always, thanks for your help

Hi,

To force a user to download a file instead of viewing it, you need to change the header of your page to tell the browser that the file needs downloading. The best way to do this is to create a custom download page.

First I would create a separate multi record section to store the vcards in, with an upload field that allows one upload per record. Otherwise you have to let the download script have direct access to the download section which could be unsafe.

The download list page could look something like this:

<?php header('Content-type: text/html; charset=utf-8'); ?>
<?php
/* STEP 1: LOAD RECORDS - Copy this PHP code block near the TOP of your page */

// load viewer library
$libraryPath = 'cmsAdmin/lib/viewer_functions.php';
$dirsToCheck = array('C:/wamp/www/','','../','../../','../../../');
foreach ($dirsToCheck as $dir) { if (@include_once("$dir$libraryPath")) { break; }}
if (!function_exists('getRecords')) { die("Couldn't load viewer library, check filepath in sourcecode."); }

$whereString = '';

// load record from 'vcards'
list($vcards, $blogMetaData) = getRecords(array(
  'tableName' => 'vcards',
  'where' => $whereString,
  'allowSearch' => false,
));

//List all of the vcards with uploads

foreach($vcards as $record){
  if($upload = (@$record['uploads'][0])){
    echo '<a href="download.php?num='.$record['num'].'" >'.$upload['info1'].'</a><br>';
  }
}

So the script grabs all of the records for the vcards section, checks if the record has an uploaded file, and if it does it displays the download link, it's also passing over the vcards num value in the URL.

Next you need to create the download script, you can read more on how the system works here:

http://php.net/manual/en/function.readfile.php

The script should look something like this:

<?php header('Content-type: text/html; charset=utf-8');


// load viewer library
$libraryPath = 'cmsAdmin/lib/viewer_functions.php';
$dirsToCheck = array('C:/wamp/www/','','../','../../','../../../');
foreach ($dirsToCheck as $dir) { if (@include_once("$dir$libraryPath")) { break; }}
if (!function_exists('getRecords')) { die("Couldn't load viewer library, check filepath in sourcecode."); }


// load file
$productNum = (int) @$_REQUEST['num'];
list($fileRecords, $filesMetaData) = getRecords(array(
  'tableName' => 'vcards',
  'where' => whereRecordNumberInUrl(0),
  'loadCreatedBy' => false,
  'allowSearch' => false,
));
$file = @$fileRecords[0];


// get download path
$filePath = '';
if (is_array( @$file['uploads'] )) {
  $download = array_pop($file['uploads']);
  $filePath = $download['filePath'];
}

// error checking
$filesize = @filesize($filePath);
if (!$file) { die("Error: No file matches that record number!"); }
if (!$filePath) { die("Error: Can't find download file!"); }
if (!file_exists($filePath)) { die("Error: Filepath doesn't exist!"); }
if (!$filesize) { die("Error: File is zero bytes!"); }

// send download
header('Content-Description: File Transfer'); // headers from http://us3.php.net/manual/en/function.readfile.php
header('Content-Type: application/octet-stream');
header('Content-Disposition: attachment; filename="' .basename($filePath). '"');
header('Content-Transfer-Encoding: binary');
header('Expires: 0');
header('Cache-Control: must-revalidate, post-check=0, pre-check=0');
header('Pragma: public');
header('Content-Length: ' . $filesize);
readfile($filePath);
exit;


?>

The download script checks if there is a number value in the URL, and gets the appropriate vcard entry from the vcards section. Then it checks if there is an associated fie, finally the script creates the download headers.

I've attached the download script to this post.

Let me know if you have any questions.

Thanks!

Greg

Greg Thomas







PHP Programmer - interactivetools.com

That's one of the most complete solutions ever answered!

I have one slight twist on it however.  Law Firm website, and the vcard is on a details page.  For each person -one image, one vcard, one description etc

Here is a build page in progress   http://khwebtemp.com/html/attorneys_details.php?Jeffrey-D.-Horst-1

Thanks as always,

Sean

Hi Sean,

The easiest way around this would be to add a drop-down list field to the person section where you can assign a vcard record to them. You can read how to do this here:

http://interactivetools.com.cmsb.me/kb/article.php?Populate-a-list-field-from-another-section-15

If you use the num field for the option values, then you can could create the download link like this:

<a href="download.php?num=<?php echo $attorneysRecord[' listFieldName']; ?>">Download Vcard</a>

Thanks!

Greg

Greg Thomas







PHP Programmer - interactivetools.com

<Files *.vcf>
  ForceType application/octet-stream
  Header set Content-Disposition attachment
</Files>

I ended up using .htaccess, not sure if it has problems but it certainly is the short answer......

I thank you for your help.

By Mikey - April 15, 2013

Greg,

Got any suggestions on how I can get to work on a category list page where as each category listed may have multiple downloadable files?

Thanks Zick

HI Zick. 

I've spotted the problem, and amended the code below:

<?php header('Content-type: text/html; charset=utf-8'); ?>
<?php
  
  // load viewer library
  $libraryPath = 'cmsbuilder/lib/viewer_functions.php';
  $dirsToCheck = array('/var/www/host/00.00.000.00/httpdocs/domain/','','../','../../','../../../');
  foreach ($dirsToCheck as $dir) { if (@include_once("$dir$libraryPath")) { break; }}
  if (!function_exists('getRecords')) { die("Couldn't load viewer library, check filepath in sourcecode."); }

  // load records from 'white_papers'
  list($white_papersRecords, $white_papersMetaData) = getRecords(array(
    'tableName'   => 'white_papers',
    'loadUploads' => true,
    'allowSearch' => false,
  ));

?>

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
 <head>
  <title></title>
  <meta http-equiv="Content-Type" content="text/html;charset=UTF-8" />
  
 </head>
<body>

    <h1>White Papers - List Page Viewer</h1>
    <?php foreach ($white_papersRecords as $record): ?>
      
      <h3><?php echo htmlencode($record['name']) ?></h3>
      Content: <?php echo $record['content']; ?><br/>
      
      <?php foreach ($record['documents'] as $index => $upload): ?>
              <p><a href="white_paper_downloads.php?num=<?php echo $record['num']; ?>" ><?php echo $upload['info1']; ?></a><br>'; ?></p>
      <?php endforeach ?>

      <hr/>
    <?php endforeach ?>

    <?php if (!$white_papersRecords): ?>
      No White Papers were found!<br/><br/>
    <?php endif ?>

</body>
</html>

The problem is that the $upload variable in the foreach loop was being overwritten by the second $upload with this if statement:  if($upload = (@$record['documents'][0])) { .

It looks as if you might also have to change the code for the download system as well, as at the moment it looks as if it just downloads the first file in the record as opposed to selecting the file that the user has selected. 

Let me know if you have any questions.

Thanks!

Greg

Greg Thomas







PHP Programmer - interactivetools.com