Working with File Uploads

enters table now… :)what was that “enctype”??? and about the download part, how could we display the files for the users to download them later on?

and i was wondering, could we like impose a size limit to the file upload size, maybe like 10MB?

enctype

As for imposing a [fphp]filesize[/fphp] restriction, just get the value of the filesize($uploadedfile) and compare it to 10485760 (10 * 1024 * 1024).

As listing them, you’ve created scenarios where you grab the records for a studentid from a table and display them on the page, so just look at those and you’ll be fine.

right, i was also thinking of doing a similar thing, but the only thing i m not sure about is, will the filenames be clickable if i did a for loop that echos the name from the table( ie. will they download when clicked on)??

Make it a link to a download page, passing the name of the file. The download page would then read the blob column and mime type and generate the appropriate header records to feed the file back to the user.

hi, i started working on the download part, and i made a couple of files and i’m completely lost now in my own work… this is what i currently understand of my work, i ve got three files, two controllers and a template

  1. download.php controller my main download file containing code similar to what is in this link you gave me http://webdesign.about.com/od/php/ht/force_download.htm
    my code for this file is thus
<?php

   // display errors, warnings, and notices
    ini_set("display_errors", true);
    error_reporting(E_ALL);

    // requirements
    require("../includes/constants.php");
    require("../includes/functions.php");


    // enable sessions
     session_start();

         // require authentication for most pages
    if (!preg_match("{(?:login|logout|register)\\.php$}", $_SERVER["PHP_SELF"]))
    {
        if (empty($_SESSION["id"]))
        {
            redirect("login.php");
        }
    }

$file= query("SELECT * FROM files where studentid= ? AND name = ?", $_SESSION['id'], $row['name']);
$blob = $file['file'];
$mimetype = $file['mimetype'];

header("Content-disposition: attachment; filename= $blob");
header("Content-type: $mimetype");
readfile("$blob");
?>

the other two files are concerned with diplaying the forms where students can view the files theyve uploaded and select the ones they wish to download via “dowload.php” file…

  1. dloadfile.php controller
<?php

    // display errors, warnings, and notices
    ini_set("display_errors", true);
    error_reporting(E_ALL);

    // requirements
    require("../includes/constants.php");
    require("../includes/functions.php");

    // enable sessions
     session_start();

         // require authentication for most pages
    if (!preg_match("{(?:login|logout|register)\\.php$}", $_SERVER["PHP_SELF"]))
    {
        if (empty($_SESSION["id"]))
        {
            redirect("login.php");
        }
    }

    $rows = query("SELECT * FROM files");
        // render form
       render("dloadfile_form.php", ["title" => "Download", "rows" => $rows]);


?>

&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&
3. dloadfile_form.php


<h1> <strong> DOWNLOAD FILES </strong></h1>


<form action="dloadfile.php" method="post" enctype="multipart/form-data">
    <fieldset>
      <div class="control-group">
             <?php
	                foreach ($rows as $row)	{ ?>

                       <li><?php echo $row['name']; ?></li>
                     <a href="download.php"> Download file</a>

                   <?php } ?>
        </div>
    </fieldset>
</form>

so what happens at the moment is when i open the dloadfile.php paeg, a list of the files uploaded by the student appears, with a link next to each file “Download file”… but when i click on this link, instead of downloading the file, it downloads a file called download.php… confused…

Actually that was a very good first attempt (nearly nailed it!).

First, on your download.php

header("Content-disposition: attachment; filename= $blob");

This line $blob should be the name of the file being requested (more on that later).

Currently, you are calling download.php, but you are not providing the file that needs to be downloaded. Change it to this:

                       <li><?php echo $row['name']; ?></li> 
                     <a href="download.php?name=<?php echo urlencode($row['name']); ?>"> Download file</a>

Then in your download.php you need to read that filename to use in your query (place this above your query).

$nameOfFile = urldecode($_GET['name']);

Then update your query to use the new variable

$file= query("SELECT * FROM files where studentid= ? AND name = ?", $_SESSION['id'], $nameOfFile);

Last, use that same variable for the filename=

header("Content-disposition: attachment; filename=$nameOfFile");

i did those changes, and it got a bit better, it now downloads the an html page for the file, and when i open the html page, i get the following errors…
at first i was getting these errors like undefined index file and undefined index mimetype in download.php, so i changed my code thus:

$nameOfFile = urldecode($_GET['name']);
$file= query("SELECT * FROM files where studentid= ? AND name = ?", $_SESSION['id'], $nameOfFile);
$blob = $file['0']['file'];            //////////////////////////////////THIS WAS  $file['file'] at first
$mimetype = $file['0']['mimetype'];          ////////////////////////////// THIS TOO

header("Content-disposition: attachment; filename=$nameOfFile");
header("Content-type: $mimetype");
readfile("$blob");
?>

and then i tried dloading again and still got an html file, capsule4.html (capsule4.png is the file i tried downlaoding) and these errors…

Undefined offset: 0 in /vhosts/localhost/html/download.php on line 25
Undefined offset: 0 in /vhosts/localhost/html/download.php on line 26

readfile(): Filename cannot be empty in /vhosts/localhost/html/download.php on line 30

line 25 $blob = $file[‘0’][‘file’];
line 26 $mimetype = $file[‘0’][‘mimetype’];

line 30 readfile(“$nameOfFile”); ////////i changed to this from readfile(“$blob”); but still got a third error…

Ah, I missed that.

$nameOfFile = urldecode($_GET['name']); 
$file= query("SELECT * FROM files where studentid= ? AND name = ?", $_SESSION['id'], $nameOfFile);
$blob = $file[0]['file'];            //remove the quotes from the [0]
$mimetype = $file[0]['mimetype'];  //remove the quotes from the [0]

header("Content-disposition: attachment; filename=$nameOfFile"); 
header("Content-type: $mimetype");
echo $blob; // Use echo, not readfile or file_get_contents, as you have the binary already stored
?>

got that, but it’s still giving a .html file with the error Undefined offset: 0 at line 25 and 26

Okay, time to do some debugging - add the var_dump line

$nameOfFile = urldecode($_GET['name']); 
$file= query("SELECT * FROM files where studentid= ? AND name = ?", $_SESSION['id'], $nameOfFile);
var_dump($file, $nameOfFile, $_SESSION['id']); die(); // add this line!
$blob = $file[0]['file'];            //remove the quotes from the [0]
$mimetype = $file[0]['mimetype'];  //remove the quotes from the [0]

header("Content-disposition: attachment; filename=$nameOfFile"); 
header("Content-type: $mimetype");
echo $blob; // Use echo, not readfile or file_get_contents, as you have the binary already stored
?>  

lol…sherlock, got this…

array (size=0)
empty
string ‘CA’ (length=2)
int 46

Okay, so your query is returning 0 records. It is searching for studentid = 46 and name = ‘CA’. I somehow, doubt that is the name of your file in the table for studentid 46.

Can you provide me with the actual name of your file?
Also, when you are on the dloadfile.php page, right-click, perform a view-source and copy and paste the HTML here. I want to see exactly what was built using the logic I helped you create for that page.

this is what i’ve got on the dloadfile.php excluding the headers…


<h1> <strong> DOWNLOAD FILES </strong></h1>
 

<form action="dloadfile.php" method="post" enctype="multipart/form-data">
    <fieldset>
      <div class="control-group">
                                    
                      <li>CA</li> 
                     <a href="download.php?name=CA"> Download file</a>
                          
                                          
                      <li>CAPSUEL4</li> 
                     <a href="download.php?name=CAPSUEL4"> Download file</a>
                          
                                          
                      <li>capsule</li> 
                     <a href="download.php?name=capsule"> Download file</a>
                          
                                          
                      <li>construction</li> 
                     <a href="download.php?name=construction"> Download file</a>
                          
                                          
                      <li>glyphicons</li> 
                     <a href="download.php?name=glyphicons"> Download file</a>
                          
                                          
                      <li>logo3</li> 
                     <a href="download.php?name=logo3"> Download file</a>
                          
                      
        </div>
    </fieldset>
</form>

and actually my table looks thus… and the name of the actual file was capsule2.png and in my upload page, i gave it the name CA

Okay, so the studentid is 0 in the table, but the script is trying to use 46. Are you sure you are inserting the correct studentid into the table?

In your upload_form.php

There is a line similar to this:

    $upload=query("INSERT INTO files (mimetype, file, name) VALUES (?, ?, ?)", $mimetype, file_get_contents($fileuploaded), $_POST['filename']); 

It needs to have studentid in it

    $upload=query("INSERT INTO files (studentid, mimetype, file, name) VALUES (?, ?, ?)", $_SESSION['id'], $mimetype, file_get_contents($fileuploaded), $_POST['filename']); 

Also, if you provide the upload controller and template, there may be other changes needed to make this work properly.

well, i tried downloading with that changed, and i’m now getting the actual file instead of an html , but the images dont seem to download properly…for eg.gif image is truncated,

my upload.php file is thus

<?php
  
   // display errors, warnings, and notices
    ini_set("display_errors", true);
    error_reporting(E_ALL);

    // requirements
    require("../includes/constants.php"); 
    require("../includes/functions.php");
   
 
    // enable sessions
     session_start();
     
         // require authentication for most pages
    if (!preg_match("{(?:login|logout|register)\\.php$}", $_SERVER["PHP_SELF"]))
    {
        if (empty($_SESSION["id"]))
        {
            redirect("login.php");
        }
    }
    
    render("upload_form.php", ["title" => "Upload your stuff"]);
    
    
    if ($_SERVER["REQUEST_METHOD"] == "POST")
    {
      if(isset($_FILES))
      {
    $fileuploaded = $_FILES['upload_file']['tmp_name'];
    $mimetype = mime_content_type($fileuploaded);
    
   $upload=query("INSERT INTO files (studentid, mimetype, file, name) VALUES (?, ?, ?, ?)", $_SESSION['id'], 
   $mimetype, file_get_contents($fileuploaded), $_POST['filename']);
    
      }
      else
      aplogize("Could not upload file");
    }
    
    
    else
    render("upload_form.php", ["title" => "Upload your stuff"]);
    
?>
    
    

and upload_form.php is

<a class="btn-large btn-custom" href="dloadfile.php"><i class="icon-truck"></i>  Download</a>
<h1> <strong> UPLOAD YOUR FILES </strong></h1>
 

<form action="upload.php" method="post" enctype="multipart/form-data">
    <fieldset>
       <div class="control-group">
            <input autofocus name="filename" placeholder="Enter file name" type="text"/>
        </div>
        <div class="control-group">
        <input type="file" name="upload_file" />
        </div>
        <div class="control-group">
            <button type="submit" class="btn-large btn-custom">Upload</button>
        </div>
    </fieldset>
</form>

What do you mean by it being truncated? The filename or the physical file?

the physical file… and i got another error for jpeg, the file downloaded but when i open it, it shows an error "ERROR INTERPRATING JPEG IMAGE FILE (APPLICATION TRANSFERED TOO FEW SCANLINES) and for gif it showed the error like .gif image is truncated…

Okay, so maybe file_get_contents was a bad suggestion, instead try this:

Replace:

    $fileuploaded = $_FILES['upload_file']['tmp_name'];
    $mimetype = mime_content_type($fileuploaded);
    
   $upload=query("INSERT INTO files (studentid, mimetype, file, name) VALUES (?, ?, ?, ?)", $_SESSION['id'], 
   $mimetype, file_get_contents($fileuploaded), $_POST['filename']);

With:

    $fileuploaded = $_FILES['upload_file']['tmp_name'];
    $mimetype = mime_content_type($fileuploaded);
    $handler = fopen($fileuploaded, 'r');
    $content = fread($handler, filesize($fileuploaded));
    fclose($handler);
    
   $upload=query("INSERT INTO files (studentid, mimetype, file, name) VALUES (?, ?, ?, ?)", $_SESSION['id'], 
   $mimetype, $content, $_POST['filename']);

Also you really don’t need the user to provide the filename, you can read it from $_FILES[‘upload_file’][‘name’]

it;s still showing the same error… but it seems to be working alright for tiny files, i tried uploading a gif file of about 12kb, and it downloaded fine…

Well there are a couple of things we could do that may solve that, 1) store the file size in the table so you can use Content-Length: $filesize in a header telling the browser how much data to expect, or 2) store the file in a physical folder on the server, and store its path in the table instead of the contents of the file, and then use readfile to output the physical file when the download request is received.

I think either scenario will work and I know the second scenario will always work, as the physical file will exist and can be feed back to the user, it isn’t stored in a DB as bytes needing to be re-formed into a physical file.