Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[BUG] Wrong file url after uploading to s3 storage #1330

Open
Bitnik212 opened this issue Oct 23, 2023 · 1 comment
Open

[BUG] Wrong file url after uploading to s3 storage #1330

Bitnik212 opened this issue Oct 23, 2023 · 1 comment

Comments

@Bitnik212
Copy link

Problem:
After upload static files to s3 storage. I got 404 status code on load page.

404 errors screenshot:
image

settings.py

USE_S3 = bool(int(ENV("USE_S3")))

if USE_S3:

    class PublicStaticStorage(CustomS3Storage):
        s3_default_acl = 'public-read'
        # file_overwrite = False
        location = "static/"
        s3_access_key = ENV("STATIC_AWS_ACCESS_KEY_ID")
        s3_secret_key = ENV("STATIC_AWS_SECRET_ACCESS_KEY")
        s3_bucket_name = ENV("STATIC_AWS_STORAGE_BUCKET_NAME")
        s3_custom_domain = ENV("STATIC_AWS_S3_CUSTOM_DOMAIN")
        url_style = AddressingStyle.DOMAIN


    class PublicMediaStorage(CustomS3Storage):
        s3_default_acl = 'public-read'
        # file_overwrite = False
        s3_access_key = ENV("MEDIA_AWS_ACCESS_KEY_ID")
        s3_secret_key = ENV("MEDIA_AWS_SECRET_ACCESS_KEY")
        s3_bucket_name = ENV("MEDIA_AWS_STORAGE_BUCKET_NAME")
        s3_custom_domain = ENV("MEDIA_AWS_S3_CUSTOM_DOMAIN")
        url_style = AddressingStyle.PATH


    STATIC_URL = f'https://{ENV("STATIC_AWS_S3_CUSTOM_DOMAIN")}/aa/' 
    STATICFILES_STORAGE = 'settings.settings.PublicStaticStorage'
else:
    STATIC_URL = '/files/static/'
    STATIC_ROOT = BASE_DIR / "files" / "static"
    MEDIA_URL = '/files/media/'
    MEDIA_ROOT = BASE_DIR / "files" / "media"

STATICFILES_DIRS = [(BASE_DIR / "files"), (BASE_DIR / "site_app" / "templates" / "static")]

CustomS3Storage.py:

from enum import StrEnum

import urllib3.util
from storages.backends.s3 import S3Storage


class AddressingStyle(StrEnum):
    PATH = "path"
    DOMAIN = "virtual"
    AUTO = "auto"


class CustomS3Storage(S3Storage):

    s3_custom_domain: str
    s3_bucket_name: str
    s3_access_key: str
    s3_secret_key: str
    url_style: AddressingStyle
    s3_default_acl: str
    location: str

    def __init__(self, **settings):
        super().__init__(**settings)

    def get_default_settings(self) -> dict:
        result: dict = super().get_default_settings()
        result.update({"location": self.location})
        result.update({"custom_domain": self.s3_custom_domain})
        result.update({"bucket_name": self.s3_bucket_name})
        result.update({"access_key": self.s3_access_key})
        result.update({"secret_key": self.s3_secret_key})
        result.update({"default_acl": self.s3_default_acl})
        result.update({"addressing_style": self.url_style})
        result.update({"endpoint_url": f"https://{self.s3_custom_domain}/"})
        return result

Solution:
I override method url in my CustomS3Storage class

example:

from enum import StrEnum

import urllib3.util
from storages.backends.s3 import S3Storage


class AddressingStyle(StrEnum):
    PATH = "path"
    DOMAIN = "virtual"
    AUTO = "auto"


class CustomS3Storage(S3Storage):

    s3_custom_domain: str
    s3_bucket_name: str
    s3_access_key: str
    s3_secret_key: str
    url_style: AddressingStyle
    s3_default_acl: str
    location: str

    def __init__(self, **settings):
        super().__init__(**settings)

    def get_default_settings(self) -> dict:
        result: dict = super().get_default_settings()
        result.update({"location": self.location})
        result.update({"custom_domain": self.s3_custom_domain})
        result.update({"bucket_name": self.s3_bucket_name})
        result.update({"access_key": self.s3_access_key})
        result.update({"secret_key": self.s3_secret_key})
        result.update({"default_acl": self.s3_default_acl})
        result.update({"addressing_style": self.url_style})
        result.update({"endpoint_url": f"https://{self.s3_custom_domain}/"})
        return result

    def url(self, name, parameters=None, expire=None, http_method=None):
        url = super().url(name, parameters, expire, http_method)
        parsed_url = urllib3.util.parse_url(url)
        url_path = parsed_url.path
        if self.url_style is AddressingStyle.DOMAIN:
            new_url = f"{self.url_protocol}//{self.s3_bucket_name}.{self.s3_custom_domain}"
        else:
            new_url = f"{self.url_protocol}//{self.s3_custom_domain}"
        return f"{new_url}{url_path}"

My requirements:

asgiref==3.7.2
Django==4.2.3
django-storages==1.14
django-tinymce==3.6.1
mysqlclient==2.2.0
Pillow==10.0.1
python-dotenv==1.0.0
six==1.16.0
sorl-thumbnail==12.9.0
sqlparse==0.4.4
boto3==1.28.62
botocore==1.31.62
s3transfer==0.7.0

OS: Linux Manjaro

There my gist for this problem: https://gist.github.com/Bitnik212/c5c19df64110643e93b86816873139ac

@jschneier
Copy link
Owner

jschneier commented Oct 25, 2023 via email

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants