آگهی
article

محدود کردن لینک دانلود مستقیم فایل ها با PHP و htaccess

php-htaccess-download

همان طور که می دانیم، لینک دانلود یک فایل در وب به طور کلی به دو دسته مستقیم و غیر مستقیم تقسیم می شود، منظور از لینک های مستقیم مواردی هستند که عموما به فرمت فایل مورد نظر (به فرض zip.) ختم شده و با کلیک بر روی آنها یا وارد کردن آدرس در مرورگر، بلافاصله برای دانلود ارائه می شوند، قاعدتا تعریف و استفاده از این نوع لینک ها خیلی سخت و پیچیده نیست، اما مشکل اکثر مدیران سایت های مختلف که عمدتا نیاز به محدود کردن دانلود فایل ها یا دانلود با پرداخت هزینه و... دارند، ایجاد و مدیریت نوع دوم از لینک ها یعنی لینک های دانلود غیر مستقیم است، در این حالت کاربر با وارد کردن آدرس لینک در مرورگر (در پس زمینه و در سرور) به صفحه دیگری ارجاع داده شده و به طور مثال ابتدا وضعیت عضویت یا پرداخت هزینه دانلود لینک بررسی و سپس مجوز دسترسی (یا عدم دسترسی) به او داده می شود، از این رو در این مطلب قصد داریم نحوه انجام این کار را با استفاده از PHP و htaccess آموزش دهیم، به این امید که مفید واقع شود.

دستور htaccess برای جلوگیری از دانلود مستقیم


اگر با فایل htaccess. آشنا باشید، می دانید که با استفاده از آن می توان تنظیمات متفاوتی در سرورهای لینوکس و ویندوز (در صورت پیکره بندی مناسب) اعمال کرد که یکی از این تنظیمات، بررسی لینک درخواستی و محدود کردن حالت دانلود مستقیم آن است، در این شیوه در مرحله اول دستوری در htaccess، با استفاده از الگوی عبارات با قاعده فرمت فایل درخواستی را بررسی کرده، اگر فرمت مورد نظر جزء مواردی باشد که نمی خواهیم به صورت مستقیم دریافت شوند، آن را به صورت پارامتر به یک فایل PHP ارسال می کنیم (این کار در سرور و بدون اطلاع کاربر انجام می شود)، به نمونه زیر توجه کنید.
RewriteEngine on
RewriteRule ^file/([^/]+)(\.zip|\.pdf|\.jpg)$ /limit-direct-access/file/download.php?file=$1$2 [NC,L]
در این مثال ساده، در ریشه یا همان root سایت دایرکتوری با نام فرضی limit-direct-access ساخته ایم که درون آن یک فایل htaccess. با دستورات بالا قرار دارد، همچنین یک فولدر با نام file که درون آن فایل download.php به همراه سایر فایل های آماده برای دانلود جای گرفته اند (با دقت به قسمت دوم دستور RewriteRule، درک ترتیب قرار گرفتن فولدرها ساده می شود)، لذا در حالت عادی کاربر برای دانلود فایل فرضی test.zip از دایرکتوری file باید نمونه لینک زیر را از سرور درخواست کند:
http://localhost/limit-direct-access/file/test.zip
اما با توجه به دستور RewriteRule نوشته شده، کاربر بدون اینکه متوجه شود، لینک زیر را از سرور درخواست خواهد کرد:
http://localhost/limit-direct-access/file/download.php?file=test.zip
در واقع لینک اصلی برای سرور مفهوم لینک Rewrite شده را دارد.

بررسی مجوزها و ارسال فایل برای دانلود با PHP


قسمت اول کار که تغییر مسیر لینک کاربر از حالت مستقیم به غیر مستقیم بود به درستی انجام شد! حال نوبت PHP است که کار بررسی مجوزهای ورود، پرداخت و... را انجام دهد و در صورتی که شرایط باب طبع ما باشد، فایل را به مرورگر جهت دانلود ارسال کند، به نمونه زیر توجه کنید.
<?php
@$file_name = $_GET['file'];
//echo $file_name;

//بررسی مجوزهای کاربر
//تعیین نحوه بررسی مجوزها با توجه به برنامه و هدف شما می تواند متفاوت باشد، به طور مثال ممکن است هنگامی که کاربر مبلغ لینک را پرداخت کرده و تراکنش موفقیت آمیز باشد، ضمن نگهداری اطلاعات در دیتابیس جهت استفاده های بعدی، یک سشن آی دی تنظیم کنید، اکنون در هنگام دانلود می توان آن سشن آی دی را بررسی کرد و اگر تنظیم شده باشد، اجازه دسترسی به کاربر داد، یا برای کاربران وارد شده به سایت از همین شیوه می توان استفاده نمود.
$user_access = TRUE;

//کاربر مجاز به دانلود است
if($user_access == TRUE){
    //تابع برای بدست آوردن پسوند فایل
    function getExtension($file){
        preg_match('/\.[^\.]+$/i', $file, $ext);
        return $ext[0];
    }
    
    //نوع فایل    
    $file_type = NULL;
    switch(getExtension($file_name)){
        case 'zip':
        $file_type = 'application/zip';
        break;
        case 'pdf':
        $file_type = 'application/pdf';
        break;
        case 'jpg':
        $file_type = 'image/jpeg';
        break;                
    }
    
    //ارسال فایل به مرورگر برای دانلود
    ob_start();
    header('Content-Description: File Transfer');
    //header('Content-Type: application/octet-stream');    
    header('Content-Type: '.$file_type);
    header('Content-Disposition: attachment; filename='.$file_name);
    header('Content-Transfer-Encoding: binary');
    header('Expires: 0');
    header('Cache-Control: must-revalidate, post-check=0, pre-check=0');
    header('Pragma: public');
    header('Content-Length: '.filesize("$file_name"));
    ob_clean();
    flush();
    readfile("$file_name");
    exit;
}
?>
<!doctype html>
<html>
<head>
<meta charset="utf-8">
<title>وبگو | محدود کردن دسترسی مستقیم به لینک دانلود</title>
<!-- https://webgoo.ir -->
</head>
<body>
<?php
//کاربر مجاز به دانلود نیست
if($user_access == FALSE){
    echo 'Error!';    
}
?>
</body>
</html>
توضیح:
- همان طور که گفتیم نام فایل به صورت یک پارامتر و از طریق متد GET به فایل PHP ارسال می شود، بنابراین در خط اول متغیر file_name را با این روش مقداردهی می کنیم (که برابر با مقادیر دریافتی از پارامتر file است).
- در گام بعدی نیاز به بررسی مجوز دانلود کاربر است، نحوه انجام این مرحله کاملا دلخواه بوده و بستگی به هدف شما دارد، آنچه برای ما اهمیت دارد، مقداردهی متغیر user_access به صورت TRUE یا FALSE است، به طور مثال می توانید برای کاربرانی که تراکنش آنها موفقیت آمیز باشد، یک سشن آی دی تنظیم کنید یا فیلدی را در دیتابیس به صورت حالت پرداخت تعریف کرده و با تطبیق اطلاعات (نام کاربری و کلمه عبور)، وضعیت مجوز دانلود کاربر را بررسی کنید، در هر صورت از هر روشی که استفاده کنید نهایتا باید به نتیجه TRUE یا FALSE ختم شود، لطفا به جهت مفصل بودن این مبحث، در این رابطه به آموزش های سشن و نحوه ایجاد سیستم ورود و خروج سایت مراجعه کنید.
- پس از تعیین حالت TRUE یا FALSE برای متغیر user_access، در صورتی که مقدار TRUE باشد، یعنی کاربر مجاز به دانلود بوده و لذا باید فایل را به مرورگر ارسال کنیم، بدین منظور هم می توانید از دستور زیر برای تعیین نوع MIME فایل استفاده کنید.
header('Content-Type: application/octet-stream');
این دستور به صورت کلی است و برای اکثر فایل ها کاربرد دارد، هم می توانید به صورت اختصاصی نوع MIME فایل ها را با تابع getExtension و دستور switch مشخص کنید (این کار در نمونه کد آموزش حاضر انجام شده است).
نکته: MIME یک استاندارد تعریف شده در وب برای تعیین فایل های مختلف از نظر نوع است که می توانید لیستی از آنها را در لینک زیر مشاهده کنید.
لیست فرمت ها و معادل آنها در استاندارد MIME
- دستورات header که در ادامه تنظیم شده اند برای ارسال صحیح فایل به مرورگر و رعایت استانداردهای HTTP لازم هستند.
- تابع ob_clean و flush برای مدیریت بهتر نقل و انتقال اطلاعات و پاک کردن موارد اضافی از سرور است (ضروری نیست).
- در نهایت نیز تابع readfile فایل مورد نظر را به مرورگر ارسال کرده و دستور exit باعث پایان اجرای کدها می شود.
نکته 1: در صورتی که مقدار متغیر user_access برابر FALSE باشد، بخش دوم دستورات اجرا شده و پیغام Error نمایش داده می شود.
نکته 2: دقت کنید که به دلیل استفاده شدن از توابع header در کد، نباید قبل از اجرای این توابع هیچ نوع خروجی به مرورگر ارسال شود، این خروجی می تواند شامل BOM یا Byte Order Mark نیز شود، لذا باید مطمئن شوید که فایل PHP شما بدون BOM است که بدین منظور می توانید از برنامه Adobe Dreamweaver یا ++Notepad استفاده کرده و BOM را با ذخیره مجدد فایل حذف کنید (در نرم افزار ++Notepad باید از منوی Encoding و امکان Convert to UTF-8 without BOM استفاده کنید).

دانلود نمونه فایل های آموزش


دانلود نمونه فایل های آموزش محدود کردن  لینک دانلود مستقیم فایل ها با PHP و htaccess
sectionدسته بندی: آموزش کاربردی » PHP
related مطالب بیشتر:
» آموزش ساخت نقشه XML سایت (Sitemap) با PHP و MySQL
» ساخت فید خوان آر اس اس (RSS Feed Reader) با PHP
» ساخت منوی هوشمند با PHP و CSS
» نمایش قسمتی از متن و پاراگراف با PHP
» نمایش آمار بازدیدها با PHP بدون استفاده از دیتابیس
commentنظرات (۱۰۶ یادداشت برای این مطلب ارسال شده است)
more یادداشت های جدید بر اساس تاریخ ارسال در انتهای یادداشت های موجود نمایش داده می شوند.
نویسنده: Farzad
زمان: ۱۸:۵۶:۲۱ - تاریخ: ۱۳۹۹/۰۳/۳۰
الان وقتی فایل رو میخواد دانلود بکنه از خودش فایل میسازه ، به این صورت
file/folder1/test.zip
فایلی که میسازه و دانلود میشه:
folder1_test.zip
یعنی بعد از پوشه file هر پوشه ای باشه اونو میزاره اسم فایل و بعدش underline میزاره
پاسخ: 
با توجه به مواردی که در دستور htaccess تنظیم کرده ایم پارامتر فایل دریافتی از متد GET هم شامل دایرکتوری و هم نام اصلی فایل است، علامت / نیز در نام فایل مجاز نیست و ظاهرا مفسر PHP به صورت خودکار آن را به _ تبدیل می کند، در هر صورت خط زیر را به شکل نمونه ویرایش کنید مشکل رفع می شود:
    header('Content-Disposition: attachment; filename='.basename($file_name));
more لطفا پیش از ارسال یادداشت نکات زیر را مد نظر داشته باشید:
- مواردی که به کلی خارج از موضوع این مطلب هستند را در فرم منوی "تماس با ما" مطرح و پاسخ را از طریق ایمیل دریافت کنید.
- به سوالات کلی، مبهم، غیرضروری و مشکلاتی که تلاشی برای رفع آن نکرده باشید پاسخ کوتاه و مختصر داده خواهد شد!
- کدها و اسکریپت های طولانی را ترجیحا در یک صفحه وب آنلاین قرار دهید تا امکان تست و بررسی وجود داشته باشد.
- تمام یادداشت ها بررسی و برای هر کاربر زمان مشخصی جهت پاسخگویی در نظر گرفته می شود، لطفا از طرح سوالات متعدد در بازه زمانی کوتاه خودداری کنید.





3 × 8
 refresh
آخرین دیدگاه ها
more برای دسترسی سریع به یادداشت مربوطه می توانید از لینک مطلب در کادر زیر استفاده کنید.
form sepideh
در:
سلام. من یک لندینگ پیج رو به صفحه اصلی سایت ریدایرکت 301 کردم. اگر بخوام تمام لینک های داخلی لندینگ رو (صفحات دنباله) رو...
۱۷:۱۱:۴۰ ۱۳۹۹/۰۴/۲۳

form ا
در:
ببخشید کدها رو کجا میزنیم؟
۱۵:۳۲:۵۷ ۱۳۹۹/۰۴/۲۳

form نیلوفر
در:
سلام. من تازه کار هستم و توی سایتی که طراحی کردم یه سرچ باکس گذاشتم. ارتباط با پایگاه داده هم داره و نتایج رو نشون...
۱۲:۵۳:۴۳ ۱۳۹۹/۰۴/۲۳

form مهدی
در:
سلام من از کد زیر استفاده کردم ولی وقتی که اعمال میشه کل css های سایتم میپره.. چه باید بکنم
۰۹:۵۵:۵۰ ۱۳۹۹/۰۴/۲۳

form سلماسی
در:
سلام ایا اگر از وبسایت شخصی کسی بازدید کنیم صاحب وبسایت شماره ی ما را می بیند
۲۱:۳۱:۵۳ ۱۳۹۹/۰۴/۲۲

form mahdi
در:
سلام استاد اگه براتون زحمتی نیست یه نگاه دیگه بهش بندازید ، الان ریموو ادیت درست هستن فقط برای نمایش کلیشون که وقتی رفرش میکنیم...
۱۹:۱۳:۱۵ ۱۳۹۹/۰۴/۲۲

form mahdi
در:
سلام استاد عزیز ، میتونید یه نمونه که فقط با جاوا اسکریپت کار شده نه با فریمورکاش بهم معرفی کنید ببینم؟ من هرچی گشتم پیدا...
۱۴:۴۶:۲۴ ۱۳۹۹/۰۴/۲۲

form mahdi
در:
سلام استاد عزیز و گرامی ، یه دفتر چه یادداشت با localstorage میخوام بنویسم ولی مشکل دارم، دو تا سوال دارم ممنون میشم راهنماییم...
۱۲:۰۵:۰۷ ۱۳۹۹/۰۴/۲۲

form اریا
در:
سلام و درود بزرگوار چه دستوری باید بنویسیم تا فقط مقدارهای خاصی از ارایه بتونیم نمایش بدیم. مثلا یک ارایه 6 خونه ای...
۱۶:۴۷:۳۱ ۱۳۹۹/۰۴/۲۰

form کافه کتاب
در:
سپاس خیلی وقت بود دنبالش بودم مرسی از سایت عالیتون
۱۴:۵۱:۴۳ ۱۳۹۹/۰۴/۲۰

form سجاد
در:
دمتون گرم. خیلی زحمت کشیدید
۱۳:۴۳:۱۱ ۱۳۹۹/۰۴/۲۰

form KhashayarPrk
در:
سلام. میخوام الگو تمام کلمات داخل پرانتز رو بدون پرانتز بریزه ارایه با این کد فقط یک پرانتز رو جواب میده.
۱۳:۱۳:۴۷ ۱۳۹۹/۰۴/۲۰

form Oliver
در:
چطوری دستور background رو برای جدلمون عکسش رو تنظیم کنیم و بهش طول و عرض بدیم
۱۶:۰۷:۱۷ ۱۳۹۹/۰۴/۱۸

form محمد
در:
با عرض سلام ببخشید چطوری میتونم واسه اشتراک کاربر انقضا بزارم مثلا یک ماه داخل دیتابیس ردیف تایم رو درست کردم البته تاریخ رو تبدیل...
۱۹:۱۴:۵۹ ۱۳۹۹/۰۴/۱۵

form آیدا
در:
سلام من در حال طراحی با html هستم اما طبق فیلم آموزشی پیش میرم از یه جایی به بعد هم تغییرات اعمال نمیشه...
۲۲:۴۷:۴۴ ۱۳۹۹/۰۴/۱۴
form علی
در:
سلام اگه میشه یه تکه کدی بفرستید که کاربر نتونه عکس مورد نظر رو دانلود کنه و با نگه داشتن روی عکس فقط لینکو ببینه...
۰۹:۳۰:۴۹ ۱۳۹۹/۰۴/۱۴
form sina2000
در:
با سلام و خسته نباشید طبق کد زیر استفاده میگه متغیر Pdc برای PDO است لطفا نمونه کد برای لایبرری...
۱۵:۰۳:۵۱ ۱۳۹۹/۰۴/۱۳
form محمد امین
در:
سلام ممنون از مطلبتون ببخشید من یه سایتی با شی گرایی کامل زدم الان میخوام آدرسشو بهینه کنم آدرس الان من به این صورته:...
۱۸:۲۸:۲۴ ۱۳۹۹/۰۴/۱۱
form ترنم
در:
سلام وقتتون بخیر ببخشید من کدهای مربوط منو و زیر منو رو نوشتم ولی موقع اجرا منوی اصلی رو میاره ولی زیر منوها رو...
۱۱:۰۶:۲۹ ۱۳۹۹/۰۴/۱۱
form ا
در:
سلام چرا وقتی مطالب مربوط که تو وبلاگم هست رو جستجو میکنم نمیاره ؟؟؟
۲۰:۳۱:۰۸ ۱۳۹۹/۰۴/۰۷
form amir
در:
سلام من اگه بخوام با وارد کردن یه id داخل یک text کل اون ردیف پاک بشن باید چه کنم مثلا یک text دریافت میکنم...
۰۱:۴۸:۰۵ ۱۳۹۹/۰۴/۰۷
form محمدباقر حسیبیان
در:
من این مشکل برام پیش اومده که موقع ورود به پروژه م توی لوکال هاست wamp صفحه سفیده و هیچی رو نشون نمیده و کد...
۱۶:۴۲:۳۸ ۱۳۹۹/۰۴/۰۶
form ...
در:
سلام دو سایت نوشته ای مربوط به من رو چند سال قبل لینک دانلود قرار دادن من ازشون خواستم بردارنش ولی اینکارو نکردن سایت...
۱۷:۲۶:۴۲ ۱۳۹۹/۰۴/۰۵
form مجتبی
در:
سلام میشه یک کد جاوا رو به php تبدیل کرد تا لود سایت سریع تر بشه؟
۱۳:۰۴:۲۳ ۱۳۹۹/۰۴/۰۴
form متین
در:
سلام ؛ من تازه شروع به یادگیری کردم ؛ مطلب بسیار کاربردی بود ؛ ممنون از شما دوست عزیز.
۰۱:۵۰:۰۵ ۱۳۹۹/۰۴/۰۴
form mahdi
در:
سلام استاد عزیز و گرامی امیدوارم که حالتون خوب باشه ، اگه براتون ممکنه لطفا یه آموزش خوب در زمینه وردپرس بهم معرفی کنید ،...
۲۱:۴۸:۱۳ ۱۳۹۹/۰۴/۰۳
form امید
در:
در برنامه نویسی کاربر با چه دستوری میتواند به صفحه اصلی برگردد
۱۹:۰۶:۵۶ ۱۳۹۹/۰۴/۰۳
form ali
در:
با سلام من یک پروژه طراحی کردم میخوام که وقتی کاربر روی ادامه مطلب کلیک کرد در خود صفحه بقیه مطالب را ببینه...
۱۸:۴۷:۲۲ ۱۳۹۹/۰۴/۰۳
form علی
در:
سلام و تشکر از زحمات ارزشمند شما احتراما بنده امروز از یک شماره تلفن ثابت از طریق یک رایانه و یک موبایل به طور...
۱۸:۰۱:۱۰ ۱۳۹۹/۰۴/۰۳
form oscar
در:
درود. اگر بخوایم در نمایش تاریخ و زمان که به صورت لحظه ای خودش در صفحه رفرش میشه، فقط عبارت " Mon Jun 22 2020...
۱۵:۵۹:۳۲ ۱۳۹۹/۰۴/۰۲
form علی
در:
با سلام چطور میشه فقط یک صفحه مورد نظرمون رفرش بشه نه همه صفحات سایت
۰۰:۰۳:۴۱ ۱۳۹۹/۰۳/۳۱
form Farzad
در:
الان وقتی فایل رو میخواد دانلود بکنه از خودش فایل میسازه ، به این صورت یعنی بعد از پوشه file هر پوشه ای باشه...
۱۸:۵۶:۲۱ ۱۳۹۹/۰۳/۳۰
form Farzad
در:
سلام ، خسته نباشید من فایل هایی که در هاست میزارم به این شکل هست: یعنی مشخص نیست فایل ها تو چه پوشه...
۱۳:۳۳:۴۰ ۱۳۹۹/۰۳/۳۰
form شیما طیبی
در:
سلام خیلی مطلب مفیدی بود . همیشه به وبلاگ نویسی به چشم یه کار خسته کننده نگاه میکردم ولی خب یه مقدار نظرم تغییر کرد....
۲۳:۵۲:۳۲ ۱۳۹۹/۰۳/۲۹
form احسان
در:
سلام من یک کد اسکریپت دارم که به صورت عددی تبدیل شده است. میخواستم بپرسم چطوری میتونم اسکریپت به حالت اولیه نوشته شده برگردانم و...
۲۱:۰۲:۱۳ ۱۳۹۹/۰۳/۲۷
form علی
در:
ممنون از پاسختون اما روش بالا جهت دانلود فایل کاربرد داره . من میخواستم به صورت استریم ویدئو پخش بشه اما آدرس مستقیم ویدئو...
۰۰:۳۷:۴۶ ۱۳۹۹/۰۳/۲۷
در انتظار بررسی: ۰