مدیریت آپلود فایل‌ها در جنگو و روش‌های ذخیره‌سازی آن‌ها

این مقاله نحوه پردازش آپلود فایل‌ها در جنگو، ساختار request.FILES، روش‌های ذخیره‌سازی فایل‌ها به‌صورت دستی یا با مدل‌ها، استفاده از chunks برای جلوگیری از مصرف زیاد حافظه، و پیاده‌سازی آپلود چندفایلی با فیلدها و ویجت‌های سفارشی را توضیح می‌دهد.

آپلود فایل جنگوrequest.FILESFileField

~8 دقیقه مطالعه • بروزرسانی ۲۳ اسفند ۱۴۰۴

مقدمه

زمانی که کاربر در یک برنامه جنگو فایلی را آپلود می‌کند، محتوای فایل در request.FILES ذخیره می‌شود. این دیکشنری شامل یک کلید برای هر FileField یا ImageField ارسال‌شده از طریق فرم است. آشنایی با نحوه مدیریت آپلود فایل‌ها در جنگو برای ساخت برنامه‌های امن و کارآمد ضروری است.


توجه داشته باشید که پذیرش فایل از کاربران غیرقابل‌اعتماد می‌تواند خطرات امنیتی ایجاد کند. برای کاهش این خطرات، به مستندات امنیتی جنگو درباره User-uploaded content مراجعه کنید.


آپلود فایل ساده

یک فرم ساده با یک FileField را در نظر بگیرید:


from django import forms

class UploadFileForm(forms.Form):
    title = forms.CharField(max_length=50)
    file = forms.FileField()

پس از ارسال فرم، فایل آپلودشده در request.FILES قابل دسترسی است. برای مثال:

request.FILES['file']


request.FILES فقط زمانی شامل داده خواهد بود که:

  • درخواست با متد POST ارسال شده باشد.
  • حداقل یک فایل واقعاً ارسال شده باشد.
  • فرم دارای ویژگی enctype="multipart/form-data" باشد.

اتصال فایل آپلودشده به فرم

برای پردازش فایل، باید request.FILES را به سازنده فرم ارسال کنید:


from django.http import HttpResponseRedirect
from django.shortcuts import render
from .forms import UploadFileForm
from somewhere import handle_uploaded_file

def upload_file(request):
    if request.method == "POST":
        form = UploadFileForm(request.POST, request.FILES)
        if form.is_valid():
            handle_uploaded_file(request.FILES["file"])
            return HttpResponseRedirect("/success/url/")
    else:
        form = UploadFileForm()
    return render(request, "upload.html", {"form": form})

ذخیره‌سازی دستی فایل

یک روش رایج برای ذخیره فایل، نوشتن آن به‌صورت تکه‌تکه است:


def handle_uploaded_file(f):
    with open("some/file/name.txt", "wb+") as destination:
        for chunk in f.chunks():
            destination.write(chunk)

استفاده از chunks() مانع از مصرف بیش از حد حافظه هنگام آپلود فایل‌های بزرگ می‌شود.


مدیریت فایل‌های آپلودی با مدل

اگر مدل شما شامل یک FileField باشد، استفاده از ModelForm فرآیند ذخیره‌سازی فایل را ساده می‌کند. فایل به‌طور خودکار در مسیر مشخص‌شده در upload_to ذخیره می‌شود.


from django.http import HttpResponseRedirect
from django.shortcuts import render
from .forms import ModelFormWithFileField

def upload_file(request):
    if request.method == "POST":
        form = ModelFormWithFileField(request.POST, request.FILES)
        if form.is_valid():
            form.save()
            return HttpResponseRedirect("/success/url/")
    else:
        form = ModelFormWithFileField()
    return render(request, "upload.html", {"form": form})

اختصاص فایل به مدل به‌صورت دستی

می‌توانید فایل را مستقیماً به فیلد مدل اختصاص دهید:


from django.http import HttpResponseRedirect
from django.shortcuts import render
from .forms import UploadFileForm
from .models import ModelWithFileField

def upload_file(request):
    if request.method == "POST":
        form = UploadFileForm(request.POST, request.FILES)
        if form.is_valid():
            instance = ModelWithFileField(file_field=request.FILES["file"])
            instance.save()
            return HttpResponseRedirect("/success/url/")
    else:
        form = UploadFileForm()
    return render(request, "upload.html", {"form": form})

اختصاص فایل خارج از درخواست

می‌توانید یک شیء شبیه فایل را به‌صورت دستی اختصاص دهید:


from django.core.management.base import BaseCommand
from django.core.files.base import ContentFile

class MyCommand(BaseCommand):
    def handle(self, *args, **options):
        content_file = ContentFile(b"Hello world!", name="hello-world.txt")
        instance = ModelWithFileField(file_field=content_file)
        instance.save()

آپلود چندفایلی

جنگو هنوز به‌صورت پیش‌فرض از آپلود چندفایلی با یک فیلد پشتیبانی نمی‌کند، اما می‌توان آن را با ساخت یک ویجت و فیلد سفارشی پیاده‌سازی کرد.


ساخت فیلد چندفایلی

from django import forms

class MultipleFileInput(forms.ClearableFileInput):
    allow_multiple_selected = True

class MultipleFileField(forms.FileField):
    def __init__(self, *args, **kwargs):
        kwargs.setdefault("widget", MultipleFileInput())
        super().__init__(*args, **kwargs)

    def clean(self, data, initial=None):
        single_file_clean = super().clean
        if isinstance(data, (list, tuple)):
            result = [single_file_clean(d, initial) for d in data]
        else:
            result = [single_file_clean(data, initial)]
        return result

class FileFieldForm(forms.Form):
    file_field = MultipleFileField()

پردازش چند فایل در ویو

from django.views.generic.edit import FormView
from .forms import FileFieldForm

class FileFieldFormView(FormView):
    form_class = FileFieldForm
    template_name = "upload.html"
    success_url = "..."

    def form_valid(self, form):
        files = form.cleaned_data["file_field"]
        for f in files:
            pass
        return super().form_valid(form)

جمع‌بندی

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


مقدمه

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


محدودیت آپلود چندفایلی در سطح مدل

اگرچه می‌توان با فیلدهای سفارشی امکان آپلود چندفایلی را در سطح فرم فراهم کرد، اما این قابلیت به معنای ذخیره چند فایل در یک FileField مدل نیست. هر FileField تنها یک فایل را نگه می‌دارد، حتی اگر ویجت فرم چندفایلی باشد.


Upload Handlers در جنگو

هندلرهای آپلود در تنظیم FILE_UPLOAD_HANDLERS تعریف می‌شوند. مقدار پیش‌فرض آن شامل دو هندلر است:


[
    "django.core.files.uploadhandler.MemoryFileUploadHandler",
    "django.core.files.uploadhandler.TemporaryFileUploadHandler",
]

  • MemoryFileUploadHandler: فایل‌های کوچک را در حافظه نگه می‌دارد.
  • TemporaryFileUploadHandler: فایل‌های بزرگ را روی دیسک ذخیره می‌کند.

این دو هندلر رفتار پیش‌فرض جنگو را تشکیل می‌دهند. با این حال، می‌توان هندلرهای سفارشی نوشت تا رفتار آپلود را تغییر داد؛ مانند اعمال محدودیت حجم، فشرده‌سازی، نمایش نوار پیشرفت، یا ارسال مستقیم فایل به یک سرویس خارجی.


محل ذخیره‌سازی داده‌های آپلودی

قبل از ذخیره‌سازی نهایی فایل، جنگو باید داده‌ها را در جایی نگه دارد:


  • اگر فایل کمتر از ۲.۵ مگابایت باشد، در حافظه ذخیره می‌شود.
  • اگر فایل بزرگ‌تر باشد، در یک فایل موقت در مسیر سیستم (مانند /tmp) ذخیره می‌شود.

این مقادیر قابل تنظیم هستند و می‌توان رفتار آپلود را با تنظیمات مربوطه تغییر داد.


تغییر رفتار Upload Handler

چند تنظیم در جنگو رفتار آپلود را کنترل می‌کنند. برای جزئیات بیشتر به بخش File Upload Settings مراجعه کنید.


تغییر هندلرهای آپلود در لحظه

گاهی لازم است یک ویو رفتار آپلود متفاوتی داشته باشد. در این موارد می‌توان request.upload_handlers را تغییر داد. این لیست قبل از پردازش فایل‌ها قابل ویرایش است.


افزودن یک هندلر سفارشی

request.upload_handlers.insert(0, ProgressBarUploadHandler(request))

با استفاده از insert(0) هندلر جدید در ابتدای لیست قرار می‌گیرد و قبل از هندلرهای پیش‌فرض اجرا می‌شود.


جایگزینی کامل هندلرها

request.upload_handlers = [ProgressBarUploadHandler(request)]

نکات مهم

تغییر هندلرها فقط قبل از دسترسی به request.POST یا request.FILES امکان‌پذیر است. پس از شروع پردازش آپلود، تغییر هندلرها بی‌معنی است و جنگو خطا می‌دهد.


از آنجا که CsrfViewMiddleware به‌طور پیش‌فرض فعال است و به request.POST دسترسی دارد، باید از csrf_exempt استفاده کنید تا بتوانید هندلرها را تغییر دهید. سپس باید با csrf_protect امنیت را در مرحله پردازش برقرار کنید.


نمونه کد با توابع

from django.views.decorators.csrf import csrf_exempt, csrf_protect

@csrf_exempt
def upload_file_view(request):
    request.upload_handlers.insert(0, ProgressBarUploadHandler(request))
    return _upload_file_view(request)

@csrf_protect
def _upload_file_view(request):
    # Process request
    ...

نمونه کد با کلاس‌ها

from django.utils.decorators import method_decorator
from django.views import View
from django.views.decorators.csrf import csrf_exempt, csrf_protect

@method_decorator(csrf_exempt, name="dispatch")
class UploadFileView(View):
    def setup(self, request, *args, **kwargs):
        request.upload_handlers.insert(0, ProgressBarUploadHandler(request))
        super().setup(request, *args, **kwargs)

    @method_decorator(csrf_protect)
    def post(self, request, *args, **kwargs):
        # Process request
        ...

جمع‌بندی

Upload Handlerها یکی از بخش‌های کلیدی سیستم آپلود فایل در جنگو هستند. با استفاده از آن‌ها می‌توان رفتار آپلود را کنترل، سفارشی‌سازی یا حتی به‌طور کامل جایگزین کرد. این قابلیت برای پیاده‌سازی ویژگی‌هایی مانند نوار پیشرفت، محدودیت حجم، فشرده‌سازی یا ارسال مستقیم فایل به سرویس‌های خارجی بسیار کاربردی است.


نوشته و پژوهش شده توسط دکتر شاهین صیامی