Django-Storages с SFTP: GET-запросы не работают

Я пытаюсь использовать django-storages для доступа к моему "Hetzner" Storage Box (https://www.hetzner.com/storage/storage-box) используя SFTP, который должен хранить медиа данные, т.е. файлы изображений, которые пользователи моего сайта могут загружать динамически.

Соответствующая часть моего settings.py файла выглядит так:

DEFAULT_FILE_STORAGE = 'storages.backends.sftpstorage.SFTPStorage'
SFTP_STORAGE_HOST = 'username.your-storagebox.de'
SFTP_STORAGE_ROOT = '/media'

SFTP_STORAGE_PARAMS = {
'username': 'username',
'password': 'password',
'allow_agent': False,
'look_for_keys': False,
}

Странно то, что, когда пользователь загружает изображение, оно помещается в хранилище, что я могу подтвердить с помощью SFTP. Но получить изображения из хранилища не удается, ни одно изображение не отображается. Выдержка из консоли:

[03/Sep/2021 22:34:01] "GET /media/filename.jpg HTTP/1.1" 404 1962

Я смог понять, что Django все еще ищет файлы внутри моего MEDIA_DIR. Кроме того, соответствующая часть моих настроек:

MEDIA_DIR = 'media'
MEDIA_ROOT = os.path.join(BASE_DIR, MEDIA_DIR)
MEDIA_URL = '/media/'

В двух словах: Использование SFTP вроде бы работает для помещения файлов в хранилище, но получить их снова почему-то не получается.

Я надеюсь на помощь. Заранее спасибо!

Проверьте настройку django-storage

Мне кажется, что вы, возможно, забыли перенести поля в модели django? В django-storage документации на Github, у вас есть эти фрагменты кода.

From:

photo = models.FileField(
    storage=FileSystemStorage(location=settings.MEDIA_ROOT),
    upload_to='photos',
)

to:

photo = models.FileField(
    upload_to='photos',
)

Может ли это быть оно? (как упоминалось в комментарии, наличие какого-нибудь фрагмента кода очень помогло бы.

)

Доступ к SFTP

Django-storage У акта есть прокси для сохранения ваших файлов в каком-то месте. Это может быть s3 bucket, http cdn, например. Или в вашем случае SFTP сервер.

При наличии других back-end, поддерживающих протокол HTTP, получить файл обратно довольно просто. Поскольку back-end предоставит вам ссылку непосредственно на сохраненный вами контент.

Для SFTP все будет по-другому, веб-страницы не поддерживают протокол FTP. Поэтому, чтобы получить доступ к файлу, вам придется создать прокси-слой между вашими веб-страницами и FTP-сервером.

@action(methods=['get'], detail=True)
def download(self, request, pk=None):

    try:
        obj = ImageModel.objects.get(id=pk)
    except ImageModel.DoesNotExist:
        raise Http404

    # with some SFTP client
    # 1. check the file exist
    # 2. pull the file from the server
    # 3. attach it to the response with the proper header
    stream = sftp_client.open(obj.file.name)
    file = stream.read()

    type, encoding = mimetypes.guess_type(obj.file.name)
    response = HttpResponse(file, content_type=type)
    response['Content-Disposition'] = u'attachment; filename="{filename}'.format(
            filename=obj.file.name)
        return response
    raise Http404

Я хочу дополнить ответ @Paulos. Вы можете создать прокси с помощью промежуточного ПО. Создайте файл project/middleware.py и добавьте его к вашему middleware-массиву в settings.py.

Затем создайте промежуточное программное обеспечение:

import mimetypes

from storages.backends.sftpstorage import SFTPStorage
from django.http import HttpResponse



class SFTPMiddleware:
    def __init__(self, get_response):
        self.get_response = get_response
        # One-time configuration and initialization.

    def __call__(self, request):
        # Code to be executed for each request before
        # the view (and later middleware) are called.

        SFS = SFTPStorage()
        
        response = self.get_response(request)

        path = request.get_full_path()

        if SFS.exists(path):
            file = SFS._read(path)
            type, encoding = mimetypes.guess_type(path)
            response = HttpResponse(file, content_type=type)
            response['Content-Disposition'] = u'attachment; filename="{filename}"'.format(filename=path)

        return response
Вернуться на верх