Creating a one-time download link is for providing downloads with limitations. The following scenarios are suitable for showing a download link with a single usage permit.
This PHP example creates a code to show a one-time download link. It uses the MySQL database to store the download path with expiry.
The below HTML form shows a button to request a one-time download link. On clicking this button, it posts the request to the PHP to create a link on the UI to download the file resource.
<form action="" method="post">
<div class="row">
<input type="submit" name="request_link" value="Request for a download link">
</div>
</form>
When users submit a request for a one-time link, the PHP creates a file resource available for download.
This example contains a source document template in the /doc/
folder.
The copyFile()
function makes a copy of the document to a specific directory created for the user. This function returns the file path for the particular user.
<?php
function copyFile($user_id)
{
$user_directory = "user-data/" . $user_id;
if (!is_dir($user_directory)) {
mkdir($user_directory, 0644, true);
}
$file_path = $user_directory . "/example.pdf";
if (!copy("doc/example.pdf", $file_path)) {
return;
}
return $file_path;
}
?>
This example uses the MySQL database to manage the link ownership and the expiry. If the data is not sensitive, we can also achieve this by using JavaScript localStorage by tracking the click attempts on client side.
The user who requests the one-time download link can only see the link on the view.
The below PHP code receives the form post request. It gets the downloadable $file_path
from the copyLink()
function.
If there is no download link requested by the user before, it inserts the downloadable file path to the database.
Otherwise, if there is a download link in the database for the user, then it updates only is_expired = 0
to allow download.
<?php
if (!empty($_POST['request_link'])) {
$file_path = copyFile($user_id);
if ($file_path) {
if (!empty($linkResult)) {
$insertDownloadPathDetails = $linkModel->updateLinkByRecordId($linkResult[0]["id"], 0);
} else {
$insertDownloadPathDetails = $linkModel->insertDownloadLink($user_id, $file_path);
}
header("Location: index.php");
exit();
}
}
?>
The LinkModel
PHP class contains the functions to perform the database operations.
The insertDownloadLink
and updateLinkByRecordId
functions perform those query executions.
It shows how the one-time download link is saved for a user with an expiration flag.
<?php
class LinkModel
{
...
...
function insertDownloadLink($user_id, $file_path)
{
$sql = "INSERT INTO tbl_download_link(user_id,file_download_path,is_expired) VALUES(?, ?, ?)";
$paramType = 'isi';
$paramValue = array(
$user_id,
$file_path,
0
);
$insertId = $this->conn->insert($sql, $paramType, $paramValue);
return $insertId;
}
function updateLinkByRecordId($recordId, $is_expired)
{
$sql = "UPDATE tbl_download_link SET is_expired=? WHERE id=?";
$paramType = 'ii';
$paramValue = array(
$is_expired,
$recordId
);
$updateId = $this->conn->execute($sql, $paramType, $paramValue);
return $updateId;
}
}
?>
On the view page, it fetches the database result from the tbl_download_link
table.
The getLinkByUser
reads the download link to display to the browser for one-time usage.
When the user downloads the link, the getLinkByRecordId is used to get the results by the link record ID. It is used in the download.php to check the expiration and permit downloading.
<?php
require_once __DIR__ . '/lib/LinkModel.php';
$linkModel = new LinkModel();
// User id is hardcoded here/
// Plugin your authentication module.
$user_id = 3;
$linkResult = $linkModel->getLinkByUser($user_id);
?>
<?php
class LinkModel
{
private $conn;
function __construct()
{
require_once __DIR__ . "/DataSource.php";
$this->conn = new DataSource();
}
function getLinkByUser($user_id)
{
$sql = "SELECT * FROM tbl_download_link WHERE user_id=?";
$paramType = "i";
$paramValue = array($user_id);
$result = $this->conn->select($sql, $paramType, $paramValue);
return $result;
}
...
...
function getLinkByRecordId($recordId)
{
$sql = "SELECT * FROM tbl_download_link WHERE id=?";
$paramType = "i";
$paramValue = array($recordId);
$result = $this->conn->select($sql, $paramType, $paramValue);
return $result;
}
}
?>
Note: This code initiates a static user id for example. You can plugin the login authentication module which returns the logged-in user ID dynamically.
Once the one-time download link is created, the below HTML code will display it to the UI.
This code checks the link expiry before showing the link to the user. If the link is expired, then it will show an error message.
<div class="phppot-container">
<h1>How to Create a One-Time Download Link</h1>
<?php if (!empty($linkResult) && $linkResult[0]['is_expired'] == 1) { ?>
<div class="row">
<div class='phppot-message error'>The download link is expired.</div>
</div>
<?php } ?>
<?php if (!empty($linkResult) && $linkResult[0]['is_expired'] == 0) {
echo "<a href='download.php?id=" . $linkResult[0]['id'] . "'>Download</a>";
} ?>
</div>
The PHP header function is used to set the content properties to allow downloading to the browser.
Once downloaded the file, then the database table expiration flag will be turned to 1.
download.php
<?php
require_once __DIR__ . '/lib/LinkModel.php';
$linkModel = new LinkModel();
$linkResult = $linkModel->getLinkByRecordId($_GET['id']);
if (!empty($linkResult) && $linkResult[0]['is_expired'] == 0) {
// Download the file
$file_path = $linkResult[0]['file_download_path'];
header('Content-Type: application/octet-stream');
header('Content-Disposition: attachment; filename="' . basename($file_path) . '"');
readfile($file_path);
$updateLinkById = $linkModel->updateLinkByRecordId($_GET['id'], 1);
} else {
header("Location: index.php");
exit();
}
?>