PHP Web Features
Creating a Secure File Download Mechanism using PHP header() function
ওয়েব ডেভেলপমেন্টের ক্রমবর্ধমান ল্যান্ডস্কেপে, ফাইল ডাউনলোড প্রসেসকে নির্বিঘ্নে পরিচালনা করার ক্ষমতা ইউজারের ইন্টারাকশনের একটি গুরুত্বপূর্ণ দিক। আপনি একটি কন্টেন্ট ম্যানেজমেন্ট সিস্টেম, একটি ই-কমার্স প্ল্যাটফর্ম, বা একটি সাধারণ ফাইল-শেয়ারিং অ্যাপ্লিকেশন তৈরি করছেন না কেন, ফাইল গুলোকে নিরাপদ এবং দক্ষতার সাথে ডাউনলোড নিশ্চিত করা সবচেয়ে গুরুত্বপূর্ণ৷ আজকের পর্বে, আমরা ফাইল ডাউনলোডের সুবিধার্থে PHP header ফাংশনের ব্যবহার করব এবং দৃঢ় নিরাপত্তা ব্যবস্থা বাস্তবায়নের জন্য সর্বোত্তম অনুশীলনগুলি প্রয়োগ করব।
Create Frontend File file_list.html File
তো চলুন প্রথমে আমরা file_list.html নামে একটা html ফাইল তৈরি করি। যেখানে AJAX দিয়ে একটা নির্দিষ্ট ফোল্ডারের কিছু নির্দিষ্ট extension এর সবগুলো ফাইল শো করবে:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>File Download System</title> <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/tailwindcss@2.2.19/dist/tailwind.min.css"> </head> <body class="bg-gray-100 p-8"> <div class="max-w-md mx-auto bg-white p-8 rounded shadow-md"> <h1 class="text-2xl font-bold mb-4">File Download System</h1> <div id="fileList" class="mb-4"> <!-- File list will be displayed here --> </div> <div id="downloadMessage" class="mt-4 text-green-600 font-bold"></div> </div> <script src="https://code.jquery.com/jquery-3.6.0.min.js"></script> <script src="app.js"></script> </body> </html>
Create get_files.php file
এবার get_files.php নামে একটি ব্যাকএন্ড ফাইল তৈরি করব , যা সার্ভারের একটি নির্দিষ্ট ফোল্ডার থেকে নির্দিষ্ট এক্সটেনশনের ফাইল গুলোকে খুঁজে আনবে এবং ফ্রন্টএন্ডের এজাক্সের রিকোয়েস্টের প্রেক্ষিতে ডাউনলোড বাটন সহ ফাইল গুলো লিস্ট প্রদর্শনের প্রয়োজনীয় html রিটার্ন করবে। সেই সাথে একটা CSRF প্রটেকশনের জন্য CSRF Token তৈরি এবং প্রয়োগ করবে।
<?php session_start(); // Function to generate CSRF token function generate_csrf_token() { if (!isset($_SESSION['csrf_token'])) { $_SESSION['csrf_token'] = bin2hex(random_bytes(32)); } return $_SESSION['csrf_token']; } $directory = 'downloads'; // Replace with the actual path to your folder // Ensure the directory is within the allowed path $realDirectory = realpath($directory); $basePath = realpath(__DIR__ . '/downloads'); // Replace with the actual base path if (strpos($realDirectory, $basePath) !== 0) { // Directory is outside the allowed path, handle accordingly exit('Invalid directory.'); } $allowedMimeTypes = ['application/pdf', 'text/plain', 'image/jpeg', 'image/png']; // Add the allowed MIME types // Get the list of files with the specified MIME types $files = []; foreach (glob($directory . '/*') as $file) { $finfo = finfo_open(FILEINFO_MIME_TYPE); $mime = finfo_file($finfo, $file); finfo_close($finfo); if (in_array($mime, $allowedMimeTypes)) { $files[] = $file; } } // Check if there are files before rendering the HTML if (count($files) > 0) { // Display the list of files foreach ($files as $file) { $filename = basename($file); echo '<div class="flex justify-between items-center border-b py-2"> <span class="text-gray-800">' . htmlspecialchars($filename, ENT_QUOTES, 'UTF-8') . '</span> <button class="downloadBtn px-4 py-2 bg-blue-500 text-white rounded-md hover:bg-blue-600" data-filename="' . $filename . '" data-csrf_token="' . generate_csrf_token() . '"> Download </button> </div>'; } } else { echo '<p>No files found.</p>'; } ?>
Create app.js File to send and receive request for file list show and download
$(document).ready(function () { // Function to get the list of files function getFiles() { $.ajax({ type: 'POST', url: 'get_files.php', // Replace with the actual path to your PHP file success: function (response) { // Display the list of files with download buttons $('#fileList').html(response); }, error: function () { $('#fileList').html('Error occurred while fetching files.'); } }); } // Initial call to get the list of files getFiles(); // Event delegation to handle click on dynamically generated download buttons $('#fileList').on('click', '.downloadBtn', function () { var filename = $(this).data('filename'); var csrf_token = $(this).data('csrf_token'); // Make an Ajax request to initiate the file download $.ajax({ type: 'POST', url: 'download.php', // Replace with the actual path to your PHP file data: { filename: filename, csrf_token: csrf_token }, headers: { 'X-Requested-With': 'XMLHttpRequest', 'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8' }, success: function (response) { if (response === 'File not found') { $('#downloadMessage').text(response); } else { // Create a hidden form dynamically var form = $('<form action="download.php" method="post">' + '<input type="hidden" name="filename" value="' + filename + '">' + '<input type="hidden" name="csrf_token" value="' + csrf_token + '">' + '</form>'); // Append the form to the body $('body').append(form); // Submit the form form.submit(); // Remove the form from the body after submission form.remove(); } // Optionally, refresh the file list after a successful download getFiles(); }, error: function () { $('#downloadMessage').text('Error occurred during file download.'); } }); }); });
Create download.php file to process Download works in server
<?php session_start(); // Function to sanitize filenames function sanitizeFilename($filename) { return preg_replace('/[^a-zA-Z0-9_\-.]/', '', $filename); } // Function to set appropriate headers for file download function downloadFile($filePath, $originalFilename) { // Sanitize the filename $sanitizedFilename = sanitizeFilename($originalFilename); // Set headers for file download $mime = mime_content_type($filePath); // Set headers for file download header('Content-Type:'.$mime); header('Content-Disposition: attachment; filename="' . $sanitizedFilename . '"'); //header('Content-Length: ' . filesize($filePath)); //use this line only for readfile function header('Content-Transfer-Encoding: binary'); header('Expires: 0'); header('Cache-Control: must-revalidate'); header('Pragma: public'); // Clear output buffers ob_clean(); flush(); // Output the file content /* readfile($filePath); exit; */ // Use fpassthru to output the file /* $filePointer = fopen($filePath, 'rb'); fpassthru($filePointer); fclose($filePointer); */ // Use file_get_contents function $fileContents = file_get_contents($filePath); echo $fileContents; /* $filePointer = fopen($filePath, 'rb'); $outputStream = fopen('php://output', 'wb'); stream_copy_to_stream($filePointer, $outputStream); fclose($filePointer); fclose($outputStream); */ } // Verify CSRF token if (!isset($_POST['csrf_token']) || $_SESSION['csrf_token'] !== $_POST['csrf_token']) { // Invalid CSRF token, handle accordingly (e.g., show an error) die('Invalid CSRF token'); } // Example usage: Assume $filePath is the path to the file on the server, and $originalFilename is the original filename. $filePath = __DIR__ .DIRECTORY_SEPARATOR.'downloads'.DIRECTORY_SEPARATOR.$_POST['filename']; // Replace with the actual path to your file // Check if the file exists if (file_exists($filePath)) { // Call the function to initiate file download downloadFile($filePath, $_POST['filename']); } else { // File not found, handle accordingly (e.g., display an error message) header('HTTP/1.1 404 Not Found'); echo 'File not found'; exit; } ?>