【问题标题】:Django-Storages with SFTP: GET-requests fail已解决:带有 SFTP 的 Django-Storages:GET 请求失败
【发布时间】:2021-11-02 02:50:27
【问题描述】:

编辑: 我解决了这个问题并在下面给出了答案。

我试图使用 django-storages 访问我的“Hetzner”存储盒 (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 似乎可以将文件放入存储中,但再次获取它们会以某种方式失败。

我希望能得到一些帮助。提前致谢!

编辑:根据要求,我将提供更多代码 sn-ps: models.py:

class SizeRestrictedImageField(ImageField):

    def __init__(self, *args, **kwargs):
        self.max_upload_size = kwargs.pop('max_upload_size', 0)
        super().__init__(*args, **kwargs)

    def clean(self, *args, **kwargs):
        data = super().clean(*args, **kwargs)

        file = data.file
        try:
            if file.size > self.max_upload_size:
                raise forms.ValidationError(_('Please keep filesize under %s. Current filesize %s'
                        ) % (filesizeformat(self.max_upload_size),
                        filesizeformat(file.size)))
        except AttributeError:
            logger.exception('An Exception occured while checking for max size of image upload. size: `%s`'
                             , file.size)
            pass

        return data


class ImageModel(models.Model):
    image = SizeRestrictedImageField(upload_to=POST_PIC_FOLDER, null=True, blank=True,
                              help_text="Erlaubte Dateitypen: .jpeg, .jpg, .png, .gif", max_upload_size=MAX_IMAGE_SIZE)

还有我的urls.py

urlpatterns = [
                  path('defaultsite/', defaultsite_view, name='home'),
                  path('help', help_view, name="help"),
                  path('user/', include('user.urls')),
                  path('sowi/', include('sowi.urls')),
                  path('blog/', include('blog.urls')),
                  path('chat/', include('chat.urls')),
                  path('notifications/', include('notifications.urls')),
                  path('cookies/', include('cookie_consent.urls')),
                  path('', home_view, name="home"),
                  path('about/', AboutUsView.as_view(), name="about-us"),
                  path('impressum/', impressum_view, name="imprint"),
                  path('privacy/', privacy_view, name='privacy'),
                  path('privacy/statement/', privacy_statement_view, name='privacy-statement'),
                  path('agb', agb_view, name="agb")
              ] + static(settings.STATIC_URL, document_root=settings.STATIC_ROOT) + static(settings.MEDIA_URL,
                                                                                           document_root=settings.MEDIA_ROOT)

我尝试从我的 url 模式中删除 +static(...)-part,但这似乎并没有解决问题。

【问题讨论】:

  • 嗨,Sarius,我觉得获取一些获取代码的 sn-p 会很有帮助。
  • 能否在问题中添加project.urls URL 模式代码?
  • @FaisalNazik 是的,我就是这么做的
  • @Sarius,基于这个问题我可能有另一个想法(github.com/jschneier/django-storages/issues/1045)。我认为您无法像以前那样访问您的文件。 GET /media/file.jpg 确实会将您重定向到媒体目录。您需要为 sftp 创建一个代理来获取该文件。我相信您可能会发现 So 帖子很有用stackoverflow.com/questions/50618855/…
  • 感谢@Paulo。我会尝试,如果有效,我会告诉你。

标签: django sftp django-storage python-django-storages


【解决方案1】:

检查 django-storage 设置

我觉得您可能忘记在 django 模型中迁移您的字段? 在django-storagedocumentation on Github,你有那些sn-p的代码。

发件人:

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

到:

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

可以吗? (正如评论中提到的,拥有一些 sn-p 代码会有很大帮助。

SFTP 访问

Django-storage act 有一个代理可以将您的文件保存在某个地方。我可以是一个s3 bucket,一个http cdn之类的。或者在您的情况下是 SFTP 服务器。

使用支持 HTTP 协议的其他后端,恢复文件相当容易。因为后端会为您提供直接指向您存储的内容的链接。

对于 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

【讨论】:

  • 嗨,保罗,谢谢。我已经检查过了,这并不能解决问题。
  • 如何创建这样的代理层?
【解决方案2】:

我想完成@Paulos 的回答。您可以使用中间件创建代理。创建一个文件project/middleware.py 并将其添加到settings.py 中的middleware 数组中。

然后创建中间件:

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

编辑: 实际上没有必要在每次调用时都打开一个新连接。您应该在系统启动时在config.pyready()-hook 中创建一个连接并使用这个。

【讨论】:

    猜你喜欢
    • 2016-02-22
    • 1970-01-01
    • 2013-02-08
    • 2023-04-06
    • 1970-01-01
    • 2021-03-11
    • 2020-12-17
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多