Download files without <a>

Collapse
X
 
  • Time
  • Show
Clear All
new posts
  • nathj
    Recognized Expert Contributor
    • May 2007
    • 937

    Download files without <a>

    Hi,

    I am working on a system, as you may have seen from my other posts, that allows members to download files. When they download a file I want to store the user ID and the file ID in a table along with the last download time and how many times they have downloaded.

    I have the SQL for this already - so no problems there.

    Unfortunately you can't use AJAX for this as the code in the onclick() won't fire if there is a URL in the href - the default handler for the URL is fired in stead.

    So my idea was simple. Have the link go to a php page that updates the table in the database, downloads the file and then returns the user to where they were originally.

    This leads me to my question. How do you download the file in PHP? It may a php file, an m3u file or anything for that matter.

    I've read around the low level file functions but all this will do, as far as I can tell, is put the file contents into a local variable. How can I get the file to the clients PC and store the fact that they have done so?

    Any help, suggestions or pointer greatly appreciated.

    Cheers
    nathj

    PS How do you get the email notification and signature to set for every post - I have to keep editing my posts to set it?
  • ak1dnar
    Recognized Expert Top Contributor
    • Jan 2007
    • 1584

    #2
    How do you get the email notification and signature to set for every post - I have to keep editing my posts to set it?
    Control Panel >> New Subscribed Threads >> Set the notification check box for threads, and bottom of the panel you can find instant email notifications.

    Comment

    • ronnil
      Recognized Expert New Member
      • Jun 2007
      • 134

      #3
      Hi nathj

      The answer is in fact painfully simple. What you need is headers (I don't remember your other posts so I don't know how much you have messed with this)

      this should get you going (hopefully)

      this file we call "download.p hp"
      [CODE=php]
      <?php
      /*
      We'll pretend that info about the file is stored in the table "file_table ", here we need at least to store the mimetype and the filename. The file is in this case actually stored another place on your server, but could might as well be stored inside the database. for simplicity the files are stored in the same directory as this one (download.php), with their own filename. I'd suggest you name them uniquely, e.g. give them the time uploaded as prefix
      */

      //Get the file id using $_GET
      $fid = $_GET['fid'];
      //we'll just pretend that your userid is stored in $_SESSION['uid']
      $uid = $_SESSION['uid'];

      //insert the file is downloaded
      $sql = "INSERT INTO file_downloaded (uid,fid) VALUES (" . $uid . "," . $fid . ")";
      $result = mysql_query($sq l) or die('Could not update, you can\'t have the file :P');
      //only give the user the file if the query was succesful
      if($result)
      {
      $sql = "SELECT * FROM file_table WHERE fid=" . $fid . " LIMIT BY 0,1";
      $result = mysql_query($sq l) or die('Could not find file');

      if($result && mysql_num_rows( $result) <> 0)
      {
      $row = mysql_fetch_ass oc($result);
      //Tells the browser the mime-type of the file.
      header('Content-type:' . $row['mime_type']);
      //Tells the browser the size of the file. A kind gesture to let people know how long they have to wait until downloads finish

      header('Content-length: ' . $row['file_size']);
      //Tells the browser to download no matter what and gives the correct filename in "save as"

      header('Content-disposition: attachment; Filename="' . $row['filename'] . '"');
      //reads the file and outputs it directly. you could also
      //echo fread(fopen($ro w['filename'],'r'),filesize( $row['filename']));
      //or

      //echo $row['file_content'];
      //if you stored the file in a db
      readfile($row['filename']);
      }
      }

      ?>
      [/CODE]

      This should be working (haven't tested it). You and others may use it freely :)

      Hope it helps :)
      Last edited by ronnil; Sep 5 '07, 04:24 PM. Reason: Syntax error in php example... doh!

      Comment

      • ronnil
        Recognized Expert New Member
        • Jun 2007
        • 134

        #4
        heh.. forgot to say

        You link to this file, either with onclick or with <a href=""></a>, either way you put the file id in the query like http://yoursite.com/path/to/download.php?fi d=3245

        and then you should get a download query

        Comment

        • pbmods
          Recognized Expert Expert
          • Apr 2007
          • 5821

          #5
          Heya, nathj.

          Simply add 'return false;' to the onclick handler of the anchor tag:
          [code=html]
          <a href="http://gohereifnojavas cript/" onclick="window .location.href= 'nogohereinstea d'; return false;">Check it out!</a>
          [/code]

          Comment

          • gregerly
            Recognized Expert New Member
            • Sep 2006
            • 192

            #6
            Hey Nathj,

            There is a new buzz word floating around call "hijax", the act of javascript hijacking a normal <a> tag. The idea is that you link to the file normally with the href att. so if javascript isn't available, your user will still be able to get the file. If javascript is available, it will "Hijak" the using the onclick. The key point to this is that the javascript needs to have "return false" as pbmods already mentioned. This will make the href not fire, and you can do what you want with your javascript. Personally, I think hijax is the way to go as it doesn't assume the existence of javascript, and users can still get their content if no javascript is available.

            Greg

            Comment

            • nathj
              Recognized Expert Contributor
              • May 2007
              • 937

              #7
              Hi Everyone,

              That's loads of help thank you to all of you. I think I am going to go the way Ronnil suggests. This could be exactly hat I'm after. If I get stuck I'll post back.

              Thanks again to everyone for the help. It's sent me off trawling the net and reading all sorts of stuff, so that's great.

              Cheers
              nathj

              Comment

              • gregerly
                Recognized Expert New Member
                • Sep 2006
                • 192

                #8
                Originally posted by nathj
                Hi Everyone,

                That's loads of help thank you to all of you. I think I am going to go the way Ronnil suggests. This could be exactly hat I'm after. If I get stuck I'll post back.

                Thanks again to everyone for the help. It's sent me off trawling the net and reading all sorts of stuff, so that's great.

                Cheers
                nathj
                Hey Nathj,

                I'm pretty sure that way will work, but an empty href in an <a> tag may cause validation to fail (if that's something you care about).

                Greg

                Comment

                • nathj
                  Recognized Expert Contributor
                  • May 2007
                  • 937

                  #9
                  Hi,

                  I've now had a play around and guess what - I've got a bit stuck again.

                  Here's the inside scoop:

                  1. I have a file download.php which displays the files to download. In this there is a line for the file that looks like this:
                  [html]
                  <a id="3" class="inlineLi nk" href="lib/recorddownload. php?item=3" title="What Good Looks Like - Session 1(mp3)">What Good Looks Like - Session 1(mp3)</a>
                  [/html]

                  2. I have a file recorddownload. php which records the fact that a member downloaded a file and then runs the download:
                  [php]
                  <?php
                  session_start() ;
                  /*
                  File: lib/recorddownload. php
                  Author: Nathan Davies
                  Created: 09/08/2007
                  Purpose: To record when a member downloads a file
                  */

                  // the calling file
                  $lcCallingFile = $_SERVER['HTTP_REFERER'] ;

                  // Update the database
                  if(!empty($_GET['item']))
                  {
                  require_once($_ SERVER['DOCUMENT_ROOT'].'/lib/dataobjects.php '); // any page may then create a data connection for that page. This keeps connections active only for as long as they are required
                  $loDB = new dataObject("dat abase", "user", "password", "connection ");

                  // get the variables and information relating to the supplied file ID
                  $lnFileID = $_GET['item'] ;
                  // array of extension to mime types
                  $laExtList = array (

                  // archives
                  'zip' => 'application/zip',

                  // documents
                  'pdf' => 'application/pdf',
                  'doc' => 'application/msword',
                  'xls' => 'application/vnd.ms-excel',
                  'ppt' => 'application/vnd.ms-powerpoint',

                  // executables
                  'exe' => 'application/octet-stream',

                  // images
                  'gif' => 'image/gif',
                  'png' => 'image/png',
                  'jpg' => 'image/jpeg',
                  'jpeg' => 'image/jpeg',

                  // audio
                  'mp3' => 'audio/mpeg',
                  'm3u' => 'audio/x-mpegurl',
                  'wav' => 'audio/x-wav',

                  // video
                  'mpeg' => 'video/mpeg',
                  'mpg' => 'video/mpeg',
                  'mpe' => 'video/mpeg',
                  'mov' => 'video/quicktime',
                  'avi' => 'video/x-msvideo'
                  ) ;

                  // now establish the file information from the database
                  $lcSelectFile = "
                  SELECT
                  a.name, a.URL
                  FROM download a
                  WHERE a.ID = $lnFileID" ;

                  $laFileInfo = $loDB->queryGetData($ lcSelectFile) ;

                  if($laFileInfo)
                  {
                  foreach($laFile Info as $lcFile)
                  {
                  $lcFileName = $lcFile['name'] ;
                  $lcFilePath = $lcFile['URL'] ;
                  $lcFileExt = substr($lcFileP ath, strpos($lcFileP ath, '.') + 1) ;
                  $lcMimeType = $laExtList[$lcFileExt] ;
                  }

                  // update the database
                  $lnLeaderID = $_SESSION['userID'] ;
                  // first check that the leader does not already have the download associated with them, if they do there is no reason to load it again.
                  $lcCheckSQL = "SELECT a.ID, a.frequency + 1 as newFrequency from leaderdownloada ssociation a WHERE a.leaderID = $lnLeaderID AND a.downloadID = $lnFileID" ;
                  $laAssociation = $loDB->queryGetData($ lcCheckSQL) ;
                  if($laAssociati on)
                  {
                  // update the frequency
                  $lnFrequency = $laAssociation[0]['newFrequency'] ;
                  $lcUpdate = "UPDATE leaderdownloada ssociation set frequency = $lnFrequency, editdate = now() WHERE leaderID = $lnLeaderID AND downloadID = $lnFileID" ;
                  $loDB->iQuery($lcUpda te) ;
                  }
                  else // first download
                  {
                  $lcTableName = "leaderdownload association" ;
                  $lcFieldList = "leaderID, downloadID, frequency, createdate, editdate" ;
                  $lcValueList = "$lnLeaderI D, $lnFileID, 1, now(), now()";
                  $loDB->queryInsert($l cTableName, $lcFieldList, $lcValueList, false) ;
                  }

                  // download the file
                  header("Content-Type: " . $lcMimeType);
                  header("Content-Length: " . filesize($lcFil ePath));
                  header('Content-Disposition: attachment; filename="' . $lcFileName .'"');
                  readfile($lcFil ePath);
                  }
                  }

                  // return to the calling page
                  header("Locatio n:$lcCallingPag e");

                  ?>
                  [/php]

                  The database updates perfectly - that's great, the only trouble is I get an error when I come to the downloading part of the process:
                  [html]
                  <html><head></head><body><pre >&lt;br /&gt;
                  &lt;b&gt;Warnin g&lt;/b&gt;: filesize() [&lt;a href='function. filesize'&gt;fu nction.filesize &lt;/a&gt;]: stat failed for http://www.christianle adership.org.uk/m3u/eddiegibbs_sess ion1.m3u in &lt;b&gt;/homepages/29/d210214908/htdocs/lib/recorddownload. php&lt;/b&gt; on line &lt;b&gt;97& lt;/b&gt;&lt;br /&gt;

                  &lt;br /&gt;
                  &lt;b&gt;Warnin g&lt;/b&gt;: Cannot modify header information - headers already sent by (output started at /homepages/29/d210214908/htdocs/lib/recorddownload. php:97) in &lt;b&gt;/homepages/29/d210214908/htdocs/lib/recorddownload. php&lt;/b&gt; on line &lt;b&gt;97& lt;/b&gt;&lt;br /&gt;
                  &lt;br /&gt;

                  &lt;b&gt;Warnin g&lt;/b&gt;: Cannot modify header information - headers already sent by (output started at /homepages/29/d210214908/htdocs/lib/recorddownload. php:97) in &lt;b&gt;/homepages/29/d210214908/htdocs/lib/recorddownload. php&lt;/b&gt; on line &lt;b&gt;98& lt;/b&gt;&lt;br /&gt;
                  http://www.christianle adership.org.uk/download/audio/eddiegibbs_sess ion1.mp3&lt;br /&gt;

                  &lt;b&gt;Warnin g&lt;/b&gt;: Cannot modify header information - headers already sent by (output started at /homepages/29/d210214908/htdocs/lib/recorddownload. php:97) in &lt;b&gt;/homepages/29/d210214908/htdocs/lib/recorddownload. php&lt;/b&gt; on line &lt;b&gt;104&lt ;/b&gt;&lt;br /&gt;
                  </pre></body></html>
                  [/html]

                  This is all a bit of mystery to me. I guess what this is saying is that headers have already been set so you can't set any more. But I'm not sure where they have been set or if they have been set.If anyone could explain this to me that would be fantastic.

                  On the plus side all these troubles are helping me to get a better understanding of how PHP works in this regard.

                  Cheers
                  nathj

                  Comment

                  • nathj
                    Recognized Expert Contributor
                    • May 2007
                    • 937

                    #10
                    Hi,

                    Well A new problem now. Having played around with the code all afternoon I can now get the download correct using headers but I can't get the database to update. Alternatively I can get the database to update but the downloaded file is a php file listing header errors because content has already been sent.

                    So here's the state of play at present:

                    The Call
                    [html]
                    <a id="3" class="inlineLi nk" href="lib/recorddownload. php?item=3&amp; id=1&amp;extra= eddiegibbs_sess ion1.m3u&amp;ex tra2=audio/x-mpegurl&amp;ext ra3=76" title="What Good Looks Like - Session 1(mp3)">What Good Looks Like - Session 1(mp3)</a>
                    [/html]

                    The PHP code:
                    [php]
                    <?php
                    /*
                    File: lib/recorddownload. php
                    Author: Nathan Davies
                    Created: 09/08/2007
                    Purpose: To record when a member downloads a file
                    */
                    // the calling file
                    $lcCallingFile = $_SERVER['HTTP_REFERER'] ;
                    $lcBaseCall = substr($lcCalli ngFile, 1, strpos($lcCalli ngfile, '?')-1) ;
                    // download the file
                    header("Content-Type: " . $_GET['extra2']);
                    header("Content-Length: " . $_GET['extra3']);
                    header('Content-Disposition: attachment; filename="' . $_GET['extra'] .'"');
                    readfile($lcFil ePath);
                    // update the db
                    require_once($_ SERVER['DOCUMENT_ROOT'].'/lib/dataobjects.php ');
                    $loDB = new dataObject("dat abase", "user", "password", "server");

                    $lnLeaderID = $_GET['id'] ;
                    // first check that the leader does not already have the download associated with them, if they do there is no reason to load it again.
                    $lcCheckSQL = "SELECT a.ID, a.frequency + 1 as newFrequency from leaderdownloada ssociation a WHERE a.leaderID = $lnLeaderID AND a.downloadID = $lnFileID" ;
                    $laAssociation = $loDB->queryGetData($ lcCheckSQL) ;
                    if($laAssociati on)
                    {
                    // update the frequency
                    $lnFrequency = $laAssociation[0]['newFrequency'] ;
                    $lcUpdate = "UPDATE leaderdownloada ssociation set frequency = $lnFrequency, editdate = now() WHERE leaderID = $lnLeaderID AND downloadID = $lnFileID" ;
                    $loDB->iQuery($lcUpda te) ;
                    }
                    else // first download
                    {
                    $lcTableName = "leaderdownload association" ;
                    $lcFieldList = "leaderID, downloadID, frequency, createdate, editdate" ;
                    $lcValueList = "$lnLeaderI D, $lnFileID, 1, now(), now()";
                    $loDB->queryInsert($l cTableName, $lcFieldList, $lcValueList, false) ;
                    }
                    ?>
                    [/php]

                    Ultimately I need to put some error trapping around this but initially I just want it to work. So at present I have the file downloading fine but the database does not get updated and I know the code itself is fine as I have tested just that part.

                    Any suggestions greatly appreciated.

                    Cheers
                    nathj

                    Comment

                    • ak1dnar
                      Recognized Expert Top Contributor
                      • Jan 2007
                      • 1584

                      #11
                      Once you call for the download headers, script execution stops.
                      So first Insert the data to the Table(s), then call for the header as the last part of the script execution.

                      Comment

                      • nathj
                        Recognized Expert Contributor
                        • May 2007
                        • 937

                        #12
                        Originally posted by ajaxrand
                        Once you call for the download headers, script execution stops.
                        So first Insert the data to the Table(s), then call for the header as the last part of the script execution.
                        Hi,

                        That makes sense. However, I'm about to go on holiday for 2 weeks and I haven't started packing yet so I better try this when I get back - I'll let you know how I get on.

                        Thanks to everyone for all the help.

                        Cheers
                        nathj

                        Comment

                        • nathj
                          Recognized Expert Contributor
                          • May 2007
                          • 937

                          #13
                          Hi,

                          Having had 2 sunny weeks Italy I have returned to the wonderful grey that only British weather can produce. So without the temptation to lounge around outside reading all day I have returned to work - my boss is most thankful.

                          I have now switched the order of the code around and all works a treat. I'm really pleased with this and very greatful for all the help I have received on this part of my project.

                          Many thanks
                          nathj

                          Comment

                          Working...