Django генерирование рандомных имён загруженным файлам FileField

В одном из моих проектов мне потребовалось реализовать загрузку пользовательских файлов через форму. Я использовал обычный FileField как советуют во всех туториалах, с стандартной конфигурацией. Пользователи загружали файлы, и все работало хорошо. Но затем я заметил проблему - делов в том что имя файла на сервере совпадало с именем файла которое загрузил пользователь. То есть, например если пользователь загружает картинку 1.png, то его картинка будет доступна по ссылке /uploads/1.png. Проблема этого подхода в том что в таких случаях имя файла можно будет просто перебрать и находить чужие файлы. Например, собрать огромный список стандартных названий файлов, и перебирать их в целях найти чужие файлы.

Чтобы исправить эту проблему нужно просто использовать случайные имена файлов. Но дело в том что по умолчанию FileField нигде не сохраняет оригинальное имя файла. Оригинальное имя файла придётся сохранять отдельным полем в моделях.

@csrf_exempt
@login_required
def upload_file(request, str):
    form = DashboardUserUploadAttachmentForm(request.POST, request.FILES)

    if form.is_valid():
        attachment = form.save(commit=False)
        attachment.filename = request.FILES['attachment'].name
        attachment.owner = request.user
        attachment.save()

        return JsonResponse({
            'status': 'ok'
        })
    else:
        return JsonResponse({'status': 'error', 'errors': form.errors})

Поэтому пришлось добавить логику забирания имени файла с запроса чтобы передавать имя файла модели отдельным полем.

def get_file_path(instance, filename):
    ext = filename.split('.')[-1]
    filename = "%s.%s" % (uuid.uuid4(), ext)
    return os.path.join('uploads/', filename)

class DashboardUserAttachments(models.Model):
    created_at = models.DateTimeField(auto_now=True)
    sent_at = models.DateTimeField(null=True, blank=True)
    attachment = models.FileField(upload_to=get_file_path)
    filename = models.CharField(max_length=128, default='')

    type_of_attachment = models.CharField(
        max_length=20,
        choices=ATTACHMENTS_TYPES
    )
    owner = models.ForeignKey(
        'DashboardUser',
        on_delete=models.CASCADE,
        related_name='user_attachments'
    )
    comment = models.TextField(default='')

После этого, каждый загруженный файл пользователей имел случайно сгенерированное имя файла. Но за тем на продакшене я столкнулся с другой проблемой: мы использовали cloudflare. А в нем максимальное тело запроса это не 100 мегабайт. Из-за этого наши пользователи начали сталкиватся с проблемами при загрузке файлов больше 100 мегабайт. Решение только 1 - убрать cloudflare для сервиса загрузки файлов. Но можно пойти более радикально и переписать всю логику загрузки файлов на чанковую. Это когда файл разбивается на кусочки на стороне клиента, и отправляется небольшими запросами на сервер, где потом этот файл соединяют в 1. Такой подход гораздо лучше потому что при чанковой загрузке можно иметь больший контроль над процессом загрузки. Например можно поставить загрузку на паузу, либо при потере соединения продолжить загрузку. Так что загружать файлы 1 запросом не стоит если вы собираетесь делать продакшен приложения, это нехорошая практика. Лучше сделать загрузку чанками.

Комментарии

Популярные сообщения из этого блога

DOS атака при помощи Python

Ведем телеграм канал через питон

Django migrations не видит изменения моделей