4 Steps Simple File Sharing With PHP MYSQL (Free Download)

Welcome to a tutorial on how to create a simple file-sharing system with PHP and MYSQL. Have some files on the server that you want to share? But not to everybody? Well, a simple “file-sharing protection” can be created using PHP and MYSQL.

The general process of a file-sharing system in PHP and MYSQL is:

  • Upload the file to a protected folder on the server.
  • Create a random hash (or download link), store it into a database table.
  • Send the download link to the user.
  • When the user accesses the download page, we verify the hash – Read the protected file and force a download.

That is the gist of it, read on for the full 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 File Share Useful Bits & Links
The End

 

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 database settings to your own in 2-lib-share.php.
  • Run 3-generate.php to create a download link, access 4-download.php?h=HASH to download the file.
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 file sharing system using PHP and MYSQL.

 

STEP 0) CREATE A PROTECTED FOLDER

The system will be meaningless if the “shared files” can be publicly downloaded by everyone. So before we even touch on the code, create a “protected shared folder” on your server. There are a number of ways to do it:

  • The easiest way is to put the protected folder outside of the public HTTP. For example, if the public HTTP folder is at /htdocs/public/, just create the protected folder at  /htdocs/shared/. Make sure that PHP still has read and write permissions to /htdocs/shared/ though.
  • If you are using shared hosting and cannot create a “private folder” – Find other ways to restrict access. For example, we can create an .htaccess file with Deny from all in Apache.
  • Alternative way – Upload and save the entire file in the 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=latin1;

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, /htdocs/shared/demo.jpg.

 

 

STEP 2) FILE SHARE PHP LIBRARY

2-lib-share.php
<?php
class FileShare {
  // (A) DATABASE CONNECTION
  // (A1) CONSTRUCTOR - CONNECT TO DATABASE
  private $pdo = null;
  private $stmt = null;
  public $error = "";
  function __construct () {
    try {
      $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
        ]
      );
    } catch (Exception $ex) { exit($ex->getMessage()); }
  }
 
  // (A2) DESTRUCTOR - CLOSE DATABASE CONNECTION
  function __destruct () {
    if ($this->stmt!==null) { $this->stmt = null; }
    if ($this->pdo!==null) { $this->pdo = null; }
  }
 
  // (B) SUPPORT FUNCTIONS
  // (B1) GENERATE RANDOM STRING (HASH)
  // CREDITS : https://stackoverflow.com/questions/4356289/php-random-string-generator
  // $length : number of characters to generate
  function random ($length=16) {
    $characters = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
    $cLength = strlen($characters);
    $random = "";
    for ($i = 0; $i < $length; $i++) {
      $random .= $characters[rand(0, $cLength - 1)];
    }
    return $random;
  }
 
  // (B2) EXECUTE SQL QUERY
  function query ($sql, $data=null) {
    try {
      $this->stmt = $this->pdo->prepare($sql);
      $this->stmt->execute($data);
      return true;
    } catch (Exception $ex) {
      $this->error = $ex->getMessage();
      return false;
    }
  }
 
  // (C) SYSTEM FUNCTIONS
  // (C1) GET SHARED FILE BY HASH
  function get ($hash) {
    if (!$this->query(
      "SELECT * FROM `file_share` WHERE `share_hash`=?", [$hash])
    ) { return false; }
    $result = $this->stmt->fetch();
    return $result===false ? null : $result ;
  }
 
  // (C2) CREATE SHARE HASH
  // $file : file to share
  // $hash : do not generate random hash, use this manual hash
  function generate ($file, $hash=null) {
    // CHECK FILE
    if (!file_exists($file)) {
      $this->error = "$file does not exist";
      return false;
    }
 
    // CREATE RANDOM HASH
    if ($hash===null) { while ($hash===null) {
      $hash = $this->random();
      if (is_array($this->get($hash))) { $hash = null; }
    }}
 
    // INSERT ENTRY
    return $this->query(
      "INSERT INTO `file_share` (`share_hash`, `share_file`) VALUES (?,?)",
      [$hash, $file]
    ) ? $hash : false ;
  }
 
  // (C3) READ FILE & FORCE DOWNLOAD
  // $hash : hash
  function download ($hash) {
    // GET HASH & CHECK
    $file = $this->get($hash);
    if (!is_array($file)) { exit("INVALID DOWNLOAD"); }
    if (!file_exists($file["share_file"])) { exit("INVALID DOWNLOAD"); }
 
    // 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"]);
  }
}
 
// (D) DATABASE SETTINGS & INIT - CHANGE TO YOUR OWN!
define("DB_HOST", "localhost");
define("DB_NAME", "test");
define("DB_CHARSET", "utf8");
define("DB_USER", "root");
define("DB_PASSWORD", "");
$_FS = new FileShare();

I know, this library looks very massive and confusing at first. But keep calm and look carefully:

  1. The constructor will automatically connect to the database when $_FS = new FileShare() is created. The destructor will close the connection.
  2. A couple of support functions.
    • random() Generates a random string of alphanumeric characters.
    • query() Runs an SQL query.
  3. The actual system functions.
    • get() Get the file download entry for a given hash.
    • generate() “Step 1” of the process. Create a random hash for the given file.
    • download() “Step 2” of the process, check a given hash and force download.
  4. Self-explanatory.

 

 

STEP 3) GENERATE DOWNLOAD LINK (HASH)

3-generate.php
<?php
// (A) LOAD LIBRARY + SETTINGS
require "2-lib-share.php";
$url = "http://your-site.com/4-download.php?h="; // download URL
$file = __DIR__ . "/shared/deer.jpg"; // use this dummy image
 
// (B) GENERATE HASH (DOWNLOAD LINK)
$hash = $_FS->generate($file);
if ($hash===false) { echo $_FS->error; }
else { echo $url . $hash ; }

Things are easy when the library is already done up – This is a simple demo on how to generate a download link. Feel free to adapt this into your own project, send the link out via email.

 

STEP 4) VERIFY & DOWNLOAD

4-download.php
// (A) LOAD LIBRARY
require "2-lib-share.php";
 
// (B) FORCE DOWNLOAD
if (isset($_GET["h"])) { $_FS->download($_GET["h"]); }

Yep… The user accesses http://site.com/4-download.php?h=HASH and this will verify the hash, force a download.

 

 

USEFUL 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, 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 upload nor file managers in this tutorial… There are plenty of free ones, 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 *