43
تشکر

نحوه آپلود فایل در PHP

نحوه آپلود فایل در PHP

نحوه آپلود فایل در PHP

این مطلب در دسته بندی مطالب پی اچ پی است. پی اچ پی را در اینجا دنبال کنید.

در سایت های زیادی امکان آپلود فایل وجود دارد. در واقع یکی از ویژگی های مهم سایت ها می باشد. در این مطلب با نحوه آپلود فایل بر روی سرور به صورت امن آشنا میشویم.

الزامات

برای آپلود فایل ابتدا باید مطمئن شوید که امکان آپلود فایل برای شما وجود دارد یا خیر که این کار را میتوانید با استفاده از تابع phpinfo انجام دهید و مقدار file_uploads را چک کنید که برابر on باشد. البته این امکان امروزه در همه وب سایت ها فعال است.

فایل های آپلود شده در PHP ابتدا به پوشه temporary منتقل میشوند که به صورت پیشفرض محل آن مشخص است. شما میتوانید از طریق فایل php.ini و مقدار upload_tmp_dir این مسیر را خودتان تغییر دهید.

HTML

برای اینکه به کاربر امکان آپلود فایل بدهید ابتدا باید یک فرم با خصوصیت enctype و مقدار multipart/form-data بسازید سپس یک input با نوع file درون آن ایجاد کنید. مانند دیگر عناصر فرم شما حتما باید خصوصیت  name را برای این عنصر مقداردهی کنید.

<h1>Upload File</h1>
<form enctype="multipart/form-data" action="result.php" method="post">
    <input type="file" name="myFile" /><br />
    <input type="submit" value="Send" />
</form>

همانطور که میدانید استایل دادن به عنصر فایل کمی سخت است. اگر طراحی برای شما مهم است به مطلب “استایل دادن به عناصر فرم – بخش دوم” مراجعه کنید.

PHP

در PHP از طریق آرایه $_FILES میتوان به اطلاعات فایل آپلود شده دسترسی داشت. که از طریق name انتخاب شده در مرحله قبل میتوان دسترسی داشت. از طریق این آرایه میتوان به اطلاعات زیر دسترسی داشت:

$_FILES[“myFile”][“name”] // نام اصلی فایل در سیستم کاربر
$_FILES[“myFile”][“type”] // ذخیره پسوند فایل
$_FILES[“myFile”][“size”] // ذخیره حجم فایل به بایت
$_FILES[“myFile”][“tmp_name”] // ذخیره نام فایل در پوشه تمپ سرور
$_FILES[“myFile”][“error”] // ذخیره خطاها در زمان انتقال فایل بین سیستم کاربر و سرور

ما بوسیله تابع move_uploaded_file میتوانیم فایل های آپلود شده را از پوشه تمپ به محلی که خودمان میخواهیم در سرور انتقال دهیم. شما همیشه باید از این تابع به جای تابع های copy و rename استفاده کنید زیرا این تابع موردهایی را چک میکند که مطمئن شود این فایل به درخواست HTTP POST دریافت شده است.

اگر میخواهید فایلی را که کاربر ارسال میکند در سرور ذخیره کنید باید بعضی از موارد را در نظر بگیرید. نام فایل باید از هرگونه کاراکتر مخصوص مانند اسلش خالی باشد. نام فایل نباید از قبل استفاده شده باشد زیرا باعث میشود فایل قبلی از بین برود.

در کد زیر ما عملیات مورد نظر را انجام میدهیم.

<?php
define("UPLOAD_DIR", "./");
  
if (!empty($_FILES&#91;"myFile"&#93;)){
    $myFile=$_FILES&#91;"myFile"&#93;;
    
    if ($myFile&#91;"error"&#93; !== UPLOAD_ERR_OK){
        echo "<p>An error occurred.</p>";
        exit;
    }
    
    // ensure a safe filename
    $name = preg_replace("/[^A-Z0-9._-]/i", "_", $myFile["name"]);
    
    // don`t overwrite an existing file
    $i = 0;
    $parts = pathinfo($name);
    while (file_exists(UPLOAD_DIR . $name)){
        $i++;
        $name = $parts["filename"] . "-" . $i . "." . $parts["extension"];
    }
    
    // preserve file form temporary directory
    $success = move_uploaded_file($myFile["tmp_name"], UPLOAD_DIR . $name);
    
    if (!$success){
        echo "<p>Unable to save file</p>";
        exit;
    }
    
    // set proper permissions on the new file
    chmod(UPLOAD_DIR . $name, 0644);
}

در خط دوم ما آدرسی که قصد ذخیره دائمی فایل را در آنجا داریم define کردیم. در خط چهارم با استفاده از یک شرط مطمئن میشویم که کاربر فایلی را ارسال کرده است. در خط هفتم با استفاده از یک شرط مطمئمن میشویم که خطایی در هنگام انتقال فایل به سرور رخ نداده باشد. در خط سیزدهم اگر در نام فایل کاربرکاراکترهایی به جز  A تا  Z و 0 تا 9 بود آن را با _ عوض میکنیم. در خطهای شانزده تا بیست و یک چک میکنیم که در مکان مقصد فایلی هم نام با فایل فعلی وجود نداشته باشد و در صورت وجود یک عدد به انتهای فایل خودمان اضافه میکنیم. در خط بیست و چهارم با استفاده از تابع نوشته شده فایل را به مقصد منتقل میکنیم. در خط بیست و شش در صورتی که خطا در هنگام انتقال از پوشه تمپ به محل دائمی ذخیره سازی وجود داشته باشد به کاربر اطلاع میدهیم. و در خط سی و دوم نوع دسترسی فایل آپلود شده را به 644 تغییر میدهیم.

در مورد اطلاع به دسترسی ها به مطلب ” نحوه کار سطح دسترسی ها (Permissions) در یونیکس و هاست ” مراجعه کنید.

ملاحظات امنیتی

در کد بالا ما هیچ کاری در زمینه امنیت آپلود فایل انجام ندادیم. برای جلوگیری از حملات هکرها از این قسمت ابتدا توصیه میکنیم که نام فایل را هنگام استفاده از تابع move_uploaded_file را به کل تغییر دهید.

همچنین شما باید پسوند فایل را نیز چک کنید. استفاده از

$_FILES[“myFile”][“type”]

به تنهایی باعث بالا بردن امنیت نمیشود زیرا به راحتی میتوان آن را تغییر داد. برای فایل های تصویری و فرمت هایی مانند GIF و JPEG و PNG و دیگر فرمت های تصویری میتوان از تابع exif_imagetype برای بررسی محتویات داخل فایل و مطمئن شدن از فرمت واقعی آن استفاده کرد. اگر نمیتوانید از این تابع استفاده کنید( برای استفاده باید EXIF فعال باشد)  میتوانید از تابع getimagesize استفاده کنید.

// verify the file is a GIF, JPEG or PNG
$fileType = exif_imagetype($_FILES["myFile"]["tmp_name"]);
$allowed = array(IMAGETYPE_GIF, IMAGETYPE_JPEG, IMAGETYPE_PNG);
if (!in_array($fileType, $allowed)){
    // file type is not permitted
    echo "<p>File Type is not permitted</p>";
    exit;
}

برای فایلهایی غیر از تصویر شما میتوانید از تابع exec برای تعیین نوع فایل با استفاده از امضاء باینری شناخته شده استفاده کنید.

// verify the file is a PDF
$mime = "application/pdf; charset=binary";
exec("file -bi " . $_FILES["myFile"]["tmp_name"], $out);
if ($out[0] != $mime){
    // file is not a PDF
    echo "<p>File Type is not permitted</p>";
    exit;
}

البته تابع mime_content_type نیز وجود دارد.

در قدم بعدی شما میتوانید مقادیر upload_max_size که بالاترین حجم آپلود کردن فایل و post_max_size که بیشترین حجم انتقال در درخواست های POST و max_file_uploads که در نسخه 5.2 اضافه شده است تعداد آپلودها را مشخص میکند در فایل php.ini تنظیم کنید.

post_max_size = 8M
upload_max_size = 2M
max_file_uploads = 20

در قدم بعدی شما میتوانید از آنتی ویروس برای چک کردن فایلهای آپلود شده به سرور استفاده کنید. اما میتوانید از کد زیر هم برای اینکار استفاده کنید(از درصد اطمینان این کد اطلاعی ندارم. دوستان هکر کلاه سفید امتحان کنند در صورت خوب بودن بگن)

exec("clamscan --stdout " . $_FILES["myFile"]["tmp_name"], $out, $return);
if ($return){
    // file is infected
    echo "<p>File is infected</p>";
    exit;
}
  • جمشیدی می‌گه:

    عالی بود ممنونم خدا خیرتون بده

  • مصطفی می‌گه:

    خوب بود ولی همش کپی از 3schools بود

  • Ali Amini می‌گه:

    سلام عالی بود.
    فقط یه سوال:
    این پترنی که بالا نوشتید اسلش رو هم قبول میکنه و به جای اسلش “_” نمیذاره!
    مشکل چیه؟

  • دریا می‌گه:

    اگه ممکنه کد حالت دوم دو بذارید واسم.مرسی

    • محسن شفیعی می‌گه:

      کدها که در همین مطلب قرار داده شده است.
      شما بعد از اینکه فایل را آپلود کردید میتونید توسط تابع های scandir و getcwd نام فایل های موجود در پوشه رو بگیرید و نمایش بدید.

  • دریا می‌گه:

    یعنی با کد پی اچ پی بدون ارتباط با دیتابیس راهی نداره؟

    • محسن شفیعی می‌گه:

      بستگی داره.
      برای مثال یک زمان کاربر ثبت نام کرده در سایت میاد یک عکس برای آواتارش آپلود میکنه، در اینجا شما باید آدرس عکس رو در فیلد همون کاربر در دیتابیس ذخیره کنید تا با آواتارهای کاربران دیگه اشتباه نشه.
      اما در یک جای دیگه شما فقط عکس آپلود میکنید و میخواید عکس ها رو به صورت تصادفی نمایش بدید که در اینجا فقط آدرس پوشه مورد نظر لازمه که خودتون دارید و نیاز به دیتابیس ندارید و کلا نیاز به دسترسی به یک عکس خاص ندارید.

  • دریا می‌گه:

    اگر بخواهیم محتوای فایل های آپلود شده داخل پوشه را نشان دهیم مثلا عکسی از داخل پوشه باید چه کدی بنویسیم؟

    • محسن شفیعی می‌گه:

      ابتدا باید در زمان آپلود مثلا عکس، آدرس اون رو در دیتابیس ذخیره کنید، بعد میتونید با برداشتن آدرس از دیتابیس عکس رو نشون بدید.

  • احسان می‌گه:

    سلام ممنون از ملب سازندتون استفاده کردیم
    فقط میشه بفرمایید exif رو چطوری میشه فعال کرد یا همین مثال رو از getimagesize برامون می زنید
    بازم تشکر

  • سجاد می‌گه:

    سلام آقا محسن
    بابت زحمتی که کشیدید ممنونم.
    مطلب مفیدی بود.
    البته کارهای دیگه ای هم هست که برای امنیت فایلی upload ای باید انجام داد که دوستان دیگه توی اینترنت گذاشتن.


  • نظرات این مطلب بسته است.