I wonder how to publishing private user files but in the way only that user can access it. I mean, after logging in, there would be many files that only the
Alright. Since I'm unformilliar with your code, I'm going to use a bit of general code as an example. All you have to do is adjust it.
First a very basic html that will upload the video / mp3 / image or whatever:
<form name="upload" action="" method="POST" ENCTYPE="multipart/form-data">
Select the file to upload: <input type="file" name="userfile">
<input type="submit" name="upload" value="upload">
</form>
Next you need to prepare your database table:
CREATE TABLE `uploads` (
`id` INT(11) NOT NULL AUTO_INCREMENT,
`userid` INT(11) NOT NULL,
`name` VARCHAR(64) NOT NULL,
`original_name` VARCHAR(64) NOT NULL,
`mime_type` VARCHAR(20) NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=0 DEFAULT CHARSET=utf8;
Now comes the file upload part. At this point I should mention that I'm not very well formilliar with MySQLi. Therefor I'm using PDO in this example. You should however be able to adjust it to MySQLi if you prefer:
<?php
session_start();
# My PDO class. A link to it can be found at the bottom of this answer
require_once 'pdo.class.php';
# Send user back login if not logged
if(!isset($_SESSION['your_login_userid_here'])){
Header("Location: your_login.php");
}
# If file is uploaded
if(isset($_POST['upload'])){
$uploaddir = 'uploads'; # Your upload directory
function tempnam_sfx($path, $suffix){
do {
$file = $path."/".mt_rand().$suffix;
$fp = @fopen($file, 'x');
}
while(!$fp);
fclose($fp);
return $file;
}
# Make a regular expression to check for allowed mime types
$pattern = "#^(image/|video/|audio/)[^\s\n<]+$#i";
if(!preg_match($pattern, $_FILES['userfile']['type']){
die("Only image, video and audio files are allowed!");
}
$uploadfile = tempnam_sfx($uploaddir, ".tmp");
if (move_uploaded_file($_FILES['userfile']['tmp_name'], $uploadfile)) {
# Define db configuration
define("DB_HOST", "localhost");
define("DB_USER", "username");
define("DB_PASS", "password");
define("DB_NAME", "dbname");
$db = new Database;
$db->query("INSERT INTO uploads SET userid=:id, name=:filename, original_name=:oriname, mime_type=:mime");
$db->bind(":userid",$_SESSION['your_login_userid_here']);
$db->bind(":filename",basename($uploadfile));
$db->bind(":oriname",basename($_FILES['userfile']['name']));
$db->bind(":mime",$_FILES['userfile']['type']);
try {
$db->execute();
} catch (PDOException $e) {
unlink($uploadfile);
die("Error saving data to the database. The file was not uploaded");
}
$id = $db->lastInsertId();
echo "File succesfully uploaded.\n";
} else {
echo "File uploading failed.\n";
}
} else {
# No upload received. Send user to upload page
Header("Location: html_upload_form.html");
}
?>
So what is happening? Basicly we are uploading the file to our upload dir where we give it a random filename with the .tmp
extension. In our database we're saving this random filename, the original filename, and what type of file it is. Ofcourse we're adding the userid as well so we know to whom to file belongs. The benefits of this approach are as follows:
Up comes the PHP file that will retrieve the uploaded file for us:
<?php
session_start();
require_once 'pdo.class.php';
# Send user back login if not logged
if(!isset($_SESSION['your_login_session_here'])){
Header("Location: your_login.php");
}
# Define db configuration
define("DB_HOST", "localhost");
define("DB_USER", "username");
define("DB_PASS", "password");
define("DB_NAME", "dbname");
$uploaddir = 'uploads/';
$id = $_GET['id'];
if(!is_numeric($id)) {
die("File id must be numeric");
}
$db = new Database;
$db->query('SELECT userid, name, mime_type FROM uploads WHERE id=:id');
$db->bind(":id", $id);
try {
$file = $db->single();
} catch (PDOException $e) {
die("Error fetching data from the database");
}
# Check if file belongs to user
if($_SESSION['your_login_session_here'] != $file['userid']){
die("This file does not belong to you!");
}
if(is_null($file) || count($file)==0) {
die("File not found");
}
$newfile = $file['original_name']; # The original filename
# Send headers and file back
header('Content-Description: File Transfer');
header('Content-Disposition: attachment; filename='.basename($newfile));
header('Expires: 0');
header('Cache-Control: must-revalidate');
header('Pragma: public');
header('Content-Length: ' . filesize($uploaddir.$file['name']));
header("Content-Type: " . $file['mime_type']);
readfile($uploaddir.$file['name']);
?>
What is happening? In this file, you're using the file id to retrieve the users file from the database. That way no user needs to know any filename at all! Thanks to our headers, the owner of the file will however be able to download the file with it's original name, without knowing the filename on the server at all.
So next I will give you a short example on how to show the user all his files:
<?php
session_start();
require_once 'pdo.class.php';
# Send user back login if not logged
if(!isset($_SESSION['your_login_session_here'])){
Header("Location: your_login.php");
}
# Define db configuration
define("DB_HOST", "localhost");
define("DB_USER", "username");
define("DB_PASS", "password");
define("DB_NAME", "dbname");
$db = new Database;
# Retrieve all files from the user and build links
$db->query("SELECT id, original_name, mime_type FROM uploads WHERE userid=:id");
$db->bind(":id",$_SESSION['your_login_session_here']);
try {
$files = $db->resultset();
} catch (PDOException $e) {
die("Error fetching data from the database");
}
if($db->rowCount() > 0){
foreach($files as $file){
echo "<a href='your_html_viewer_file.php?id=". $file['id'] "&type=". $file['mime_type'] .">". $file['original_name'] ."</a><br />";
}
} else {
echo "No files found!";
}
?>
And finally comes the PHP file that will display the file in some HTML mediaplayer. I will only 2 examples here, you should be able add your own very easily:
<?php
session_start();
# Send user back login if not logged
if(!isset($_SESSION['your_login_session_here'])){
Header("Location: your_login.php");
}
$id = $_GET['id'];
$type = $_GET['type'];
if(strpos($type, 'video/') === 0){ ?>
<video width="480" height="320" controls>
<source src="your_file_retriever.php?id=<?php echo $id; ?>" type="<?php echo $type; ?>">
</video>
<?php } else if(strpos($type, 'audio/') === 0){ ?>
<audio controls>
<source src="your_file_retriever.php?id=<?php echo $id; ?>" type="<?php echo $type; ?>">
</audio>
<?php } ?>
Now to make sure no one is going to just brute force attack your uploads folder, you need to create a .htaccess file inside this folder. The following code will block anyone from accessing that folder except the server itself ofcourse:
order deny,allow
deny from all
allow from 127.0.0.1
My PDO Class.
There are many solutions. The one I am using for a dating site is to have folder names that are the same as the unique reference for the user. All files in there belong to that user. However access is still controlled by php providing the correct links in the html.
That is the simplified answer. because I don't want the number of sub-folders in the image folder to be in the thousands, I hash them into another level of folders.