Getting private files or showing private images in a HTML page

后端 未结 2 1507
野趣味
野趣味 2020-12-06 14:52

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

相关标签:
2条回答
  • 2020-12-06 15:19

    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:

    • - No one will ever know the file name on the server.
    • - No one will ever know the original file name except the owner.
    • - The Mime Type allows us to setup our HTML5 mediaplayer.
    • - The owner is able to download the file, but no one else.

    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.

    0 讨论(0)
  • 2020-12-06 15:25

    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.

    0 讨论(0)
提交回复
热议问题