3 Steps Simple File Sharing In PHP MYSQL (Free Download)

Welcome to a tutorial on how to create a simple file-sharing system in PHP and MYSQL. Have some files on the server that you want to share? But not to everybody? Well, we can create and share a “download link” – Read on for the example!

ⓘ I have included a zip file with all the source code at the start of this tutorial, so you don’t have to copy-paste everything… Or if you just want to dive straight in.

 

 

TABLE OF CONTENTS

 

DOWNLOAD & NOTES

Firstly, here is the download link to the example code as promised.

 

QUICK NOTES

  • Create a dummy database and import 1-file-share.sql.
  • Change the URL and database settings to your own in 2-lib-share.php.
  • Run 3a-share.php to create a download link, copy-paste the link into a new tab to download.
If you spot a bug, feel free to comment below. I try to answer short questions too, but it is one person versus the entire world… If you need answers urgently, please check out my list of websites to get help with programming.

 

EXAMPLE CODE DOWNLOAD

Click here to download all the example source code, I have released it under the MIT license, so feel free to build on top of it or use it in your own project.

 

 

PHP MYSQL FILE SHARING

All right, let us now get into the example of a simple PHP MYSQL file-sharing system.

 

CREATE A PROTECTED FOLDER

This system will be meaningless if the “shared files” can be publicly accessed by anyone. So before we touch on the code, create a “protected shared folder” on your server. There are a few ways to do it:

  • The easy way is to create a shared folder outside of the public HTTP. For example, if the public HTTP folder is at /HTTP/public/, just create a shared folder at /HTTP/shared/. Make sure that PHP has read permissions for /HTTP/shared/ though.
  • If you are using Apache, create an .htaccess file in /HTTP/shared/ with just one line – Deny from all.
  • Alternatively, upload and save the files in a database.

 

STEP 1) SHARED FILES DATABASE TABLE

1-file-share.sql
CREATE TABLE `file_share` (
  `share_hash` varchar(24) CHARACTER SET utf8 COLLATE utf8_bin NOT NULL,
  `share_file` text NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

ALTER TABLE `file_share`
  ADD PRIMARY KEY (`share_hash`);

Look no further, there are only 2 fields for this table.

  • share_hash The primary key and “hash to verify download”. That is, http://site.com/download.php?h=HASH. Take note that this field is case-sensitive.
  • share_file The absolute path of the file on the server. For example, /HTTP/shared/demo.jpg.

 

 

STEP 2) FILE SHARE PHP LIBRARY

2-lib-share.php
<?php
class FileShare {
  // (A) CONSTRUCTOR - CONNECT TO DATABASE
  private $pdo = null;
  private $stmt = null;
  public $error = "";
  function __construct () {
    $this->pdo = new PDO(
      "mysql:host=".DB_HOST.";dbname=".DB_NAME.";charset=".DB_CHARSET,
      DB_USER, DB_PASSWORD, [
      PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
      PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC
    ]);
  }
 
  // (B) DESTRUCTOR - CLOSE DATABASE CONNECTION
  function __destruct () {
    if ($this->stmt!==null) { $this->stmt = null; }
    if ($this->pdo!==null) { $this->pdo = null; }
  }
 
  // (C) EXECUTE SQL QUERY
  function query ($sql, $data=null) {
    $this->stmt = $this->pdo->prepare($sql);
    $this->stmt->execute($data);
  }
 
  // (D) GET SHARED FILE BY HASH
  function getHash ($hash) {
    $this->query("SELECT * FROM `file_share` WHERE `share_hash`=?", [$hash]);
    return $this->stmt->fetch();
  }
 
  // (E) GET SHARED FILE BY FILE NAME
  function getFile ($file) {
    $this->query("SELECT * FROM `file_share` WHERE `share_file`=?", [$file]);
    return $this->stmt->fetch();
  }
 
  // (F) SHARE A FILE
  function share ($file) {
    // (F1) CHECK FILE
    if (!file_exists($file)) {
      $this->error = "$file does not exist";
      return false;
    }
    $check = $this->getFile($file);
    if (is_array($check)) { return DL_URL . $check["share_hash"]; }
  
    // (F2) CREATE/CHECK HASH
    // CREDITS : https://stackoverflow.com/questions/4356289/php-random-string-generator
    $length = 16;
    $chars = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
    $len = strlen($chars);
    while ($hash===null) {
      $random = "";
      for ($i=0; $i<$length; $i++) { $random .= $chars[rand(0, $len-1)]; }
      if (!is_array($this->get($random))) { $hash = $random; }
    }
 
    // (F3) INSERT ENTRY
    $this->query("INSERT INTO `file_share` (`share_hash`, `share_file`) VALUES (?,?)", [$hash, $file]);
    return DL_URL . $hash;
  }
 
  // (G) READ FILE & FORCE DOWNLOAD
  function download ($hash) {
    // (G1) GET HASH & CHECK
    if (!isset($_GET["h"])) { exit("Invalid download"); }
    $file = $this->getHash($_GET["h"]);
    if (!is_array($file)) { exit("Invalid download"); }
    if (!file_exists($file["share_file"])) { exit("Invalid download"); }
 
    // (G2) FORCE DOWNLOAD
    header("Content-Type: application/octet-stream");
    header("Content-Type: Content-Transfer-Encoding: Binary");
    header("Content-disposition: attachment; filename=\"".basename($file["share_file"])."\"");
    readfile($file["share_file"]);
  }
}
 
// (H) DATABASE SETTINGS - CHANGE TO YOUR OWN
define("DL_URL", "http://localhost/3b-download.php?h=");
define("DB_HOST", "localhost");
define("DB_NAME", "test");
define("DB_CHARSET", "utf8mb4");
define("DB_USER", "root");
define("DB_PASSWORD", "");
 
// (I) NEW FILE SHARE OBJECT
$_FS = new FileShare();

This library looks very massive and confusing at first, but keep calm and look carefully:

  • (A, B, I) When $_FS = new FileShare() is created, the constructor connects to the database. The destructor closes the connection.
  • (C) query() A support function to run an SQL query.
  • (D to G) There are only “4 actual system functions”.
    • getHash() Get a shared file by hash.
    • getFile() Get a shared file by file name.
    • share() Step 1 of the process, pass in the file to share and this returns the download link.
    • download() “Step 2” of the process, check a given hash and force download.
  • (H) Remember to change the settings to your own.

 

 

STEP 3) CREATE & SHARE THE DOWNLOAD LINK

3A) CREATE A DOWNLOAD LINK

3-share.php
<?php
require "2-lib-share.php";
$link = $_FS->share(__DIR__ . "/README.txt");
echo $link===false ? $_FS->error : $link;

Don’t think this needs a lot of explanation. Just call $_FS->share(FILE-TO-SHARE) and share the download link – For this demo, we will share the README.txt.

 

3B) VERIFY & DOWNLOAD

3b-download.php
<?php
require "2-lib-share.php";
$_FS->download();

Lastly, the user accesses http://site.com/3b-download.php?h=HASH. This will verify the hash and force a download.

 

 

EXTRA BITS & LINKS

That’s all for the tutorial, and here is a small section on some extras and links that may be useful to you.

 

SKELETAL SYSTEM ONLY!

The above demo works, but it is very simplistic. Most definitely not recommended to use as-it-is. A couple of “improvement ideas” here:

  • Add a “download history” table for added security. Keep track of the download file, time, and IP address.
  • We can technically have a bazillion download hash codes, but it will be nice to have some sort of clean-up mechanism. Follow up with the previous “download history” – If a file has not been downloaded for a year, delete the hash code and file (or at least notify the administrators).
  • More “download controls” – Set an expiry date and number of download times.
  • If you want more security, keep the downloads to registered users only – In download() of 2-lib-share.php, do a if (!isset($_SESSION["user"])) { exit("NOT SIGNED IN"); }.
  • Captain Obvious, there are no file uploads nor file managers in this tutorial. There are plenty of free ones, follow up with the links below.

 

LINKS & REFERENCES

 

THE END

Thank you for reading, and we have come to the end. I hope that it has helped you to better understand, and if you want to share anything with this guide, please feel free to comment below. Good luck and happy coding!

Leave a Comment

Your email address will not be published. Required fields are marked *