Now, we will make another upload script. But this time we won't save the
file in the database. We will only store the file info there but the real
file is stored in the file server. We need a little modification to the upload
table. Instead of using BLOB datatype we just use VARCHAR to store the file
path.
CREATE TABLE upload2 (
id INT NOT NULL AUTO_INCREMENT,
name VARCHAR(30) NOT NULL,
type VARCHAR(30) NOT NULL,
size INT NOT NULL,
path VARCHAR(60) NOT NULL,
PRIMARY KEY(id)
);
The HTML form we use is no different with the previous one since the real changes
will take place in the PHP codes.
<form method="post" enctype="multipart/form-data">
<table width="350" border="0" cellpadding="1"
cellspacing="1" class="box">
<tr>
<td width="246">
<input type="hidden" name="MAX_FILE_SIZE" value="2000000">
<input name="userfile" type="file" id="userfile">
</td>
<td width="80"><input name="upload" type="submit"
class="box" id="upload" value=" Upload "></td>
</tr>
</table>
</form>
Okay, now let's take a look at the upload process. First we need to specify
the directory to store the uploaded files. We store the directory name in
$uploadDir. Note that PHP must have write
access to $uploadDir or else the upload
will fail. If you're web host using a Linux server you may need to set the
permission for the upload directory to 777.
<?php
$uploadDir = 'C:/webroot/upload/';
if(isset($_POST['upload']))
{
$fileName = $_FILES['userfile']['name'];
$tmpName = $_FILES['userfile']['tmp_name'];
$fileSize = $_FILES['userfile']['size'];
$fileType = $_FILES['userfile']['type'];
$filePath = $uploadDir . $fileName;
$result = move_uploaded_file($tmpName, $filePath);
if (!$result) {
echo "Error uploading file";
exit;
}
include '../library/config.php';
include '../library/opendb.php';
if(!get_magic_quotes_gpc())
{
$fileName = addslashes($fileName);
$filePath = addslashes($filePath);
}
$query = "INSERT INTO upload2 (name, size, type, path
) ".
"VALUES ('$fileName', '$fileSize', '$fileType', '$filePath')";
mysql_query($query) or die('Error, query failed : ' . mysql_error());
include '../library/closedb.php';
echo "<br>Files uploaded<br>";
}
?>
The key here is the move_uploaded_file() function.
This function will move the uploaded files from the temporary upload directory
to the location that we earlier ( $uploadDir . $fileName
). If for some reason the functioncannot move the file it will return false
and we exit the script because continuing the script is no use.
|
Downloading
For listing the download files we just need to copy from the previous
script. The real difference start when you click on the download
link.
if(isset($_GET['id']))
{
include '../library/config.php';
include '../library/opendb.php';
$id = $_GET['id'];
$query = "SELECT name, type, size, path
FROM upload2 WHERE id = '$id'";
$result = mysql_query($query) or die('Error, query failed');
list($name, $type, $size, $filePath) = mysql_fetch_array($result);
header("Content-Disposition: attachment; filename=$name");
header("Content-length: $size");
header("Content-type: $type");
readfile($filePath);
include '../library/closedb.php';
exit;
}
After fetching the file info from the database and sending
the required headers the next thing we need to do is read the
file content from the server and send it to the browser. We can
accomplish this by using readfile()
function.
The Problems
When using this method of uploading files there are two problems
that we need to take care of. They are :
- Preventing direct access to the uploaded files
- Handling duplicate file names
Preventing direct access
For this example the upload directory where the files are stored
is /home/arman198/public_html/examples/upload/files/.
Using your browser you see the upload directory by clicking here.
This is ( usually ) a bad thing because anyone can see directly
the file list and download them all. If you don't want to prevent
people from seeing the content of the upload directory you could
create an empty file, name it index.html then put that file to
the upload directory. This is certainly not the optimal solution
because maybe some people will try guessing the files names.
A better approach is to move the upload directory away from your
web root. For example, the web root for this site is: /home/arman198/public_html/
to prevent direct listing i can set the upload directory
to /home/arman198/upload/.
This way an outsider cannot see directly what's inside the upload
directory. For example, even if you go to this url : http://www.php-mysql-tutorial.com/../upload/
you can't see the upload directory
Handling duplicate file names
When saving the files into the MySQL database we don't have to
worry about this. The table for saving the files uses an id as
the primary key so even we put ten files with the same name there
won't be any problem since we access the files using that unique
id.
The problem arise when saving the files to file server. The move_uploaded_file()
function will overwrite a file with the same name and this is
certainly not a desired behaviour.
To prevent this we just need to modify the file name. In this example
the file names are changed into a random string, 32 characters
long.
<?php
// ... same code as before
// get the file extension first
$ext = substr(strrchr($fileName, "."), 1);
// make the random file name
$randName = md5(rand() * time());
// and now we have the unique file name for the upload file
$filePath = $uploadDir . $randName . '.' . $ext;
$result = move_uploaded_file($tmpName, $filePath);
// ... same code as before
}
?>
First we extract the file extension from the file name using strrchr()
function combined with substr().
Then using md5() we generate the
32 characters long of random string. It will look something like
7d1d1da5aac5ad72b293165e8e6fe89b. After
we join them up we get the new unique file name. This way
the chance of stumbling upon a duplicate name problem is very
very slim.
As for the download part there's no change required. All we did
was change the file name on the server so the previous download
script will do just fine