Skip to main content
 首页 » 编程设计

python之Django 和 S3 直接上传

2025年05月04日53sharpest

在我的项目中,我已配置并正常工作 S3 storages .现在我正在尝试使用 s3 direct 配置直接上传到 s3 .它几乎可以正常工作。用户能够上传图像并将其存储在 S3 中。当我在数据库中保存对图像的引用时,问题就来了。

模型.py

class FullResPicture(Audit): 
    docfile = models.ImageField() 
    picture = models.OneToOneField(Picture, primary_key=True) 

settings.py

... 
S3DIRECT_DESTINATIONS = { 
    # Allow anybody to upload jpeg's and png's. 
    'imgs': ('uploads/imgs', lambda u: u.is_authenticated(), ['image/jpeg', 'image/png'], 'public-read','bucket-name'), 
} 
... 

views.py

#Doc file is the url to the image that the user uploaded directly to S3 
#https://s3-eu-west-1.amazonaws.com/bucket/uploads/imgs/picture.jpeg 
fullRes = FullResPicture(docfile = form_list[1].cleaned_data['docfile']) 

因此,如果我查看我的数据库,我有一些工作正常的图像(我仅使用 django-storages 上传的图像)具有如下 docfile 值:

images/2015/08/11/image.jpg 

当应用程序尝试访问这些图像时,S3 boto 能够正确获取图像。

但是我已经从用户的浏览器直接上传了图像。对于那些,我存储了完整的 url,因此它们在数据库中看起来像这样:

https://s3-eu-west-1.amazonaws.com/bucket/uploads/imgs/Most-Famous-Felines-034.jpg 

当应用程序尝试访问它们时,我遇到了这个异常:

File "/Users/mariopersonal/Documents/dev/venv/pictures/lib/python2.7/site-packages/django/db/models/fields/files.py", line 49, in _get_file 
    self._file = self.storage.open(self.name, 'rb') 
  File "/Users/mariopersonal/Documents/dev/venv/pictures/lib/python2.7/site-packages/django/core/files/storage.py", line 35, in open 
    return self._open(name, mode) 
  File "/Users/mariopersonal/Documents/dev/venv/pictures/lib/python2.7/site-packages/storages/backends/s3boto.py", line 363, in _open 
    name = self._normalize_name(self._clean_name(name)) 
  File "/Users/mariopersonal/Documents/dev/venv/pictures/lib/python2.7/site-packages/storages/backends/s3boto.py", line 341, in _normalize_name 
    name) 
SuspiciousOperation: Attempted access to 'https:/s3-eu-west-1.amazonaws.com/bucket/uploads/imgs/Most-Famous-Felines-034.jpg' denied. 

很明显,S3 boto 不喜欢将文件引用作为完整的 url。

出于故障排除的目的,我尝试对保存的值进行硬编码,因此它只保存最后一部分而不是完整的 url,但是当它尝试访问图像时我遇到了另一个异常:

IOError: File does not exist: uploads/imgs/Most-Famous-Felines-034.jpg 

有人知道这里出了什么问题吗?有没有人有任何直接上传到 s3 的工作示例,将对上传文件的引用存储在模型中?

谢谢。

请您参考如下方法:

这是我修复的方式,以防对其他人有所帮助。 如果您已经拥有 django-storages,则此解决方案适用工作正常django-s3direct从客户端上传图像,但您无法使它们协同工作。

使用同一个桶

我做的第一件事是确保 django-storages 和 django-s3direct 都配置为使用相同的存储桶。由于您已经让 django-storages 和 django-s3direct 分别工作,只需检查两者是否使用相同的存储桶。对于大多数用户,只需要做这样的事情:

settings.py

... 
S3DIRECT_DESTINATIONS = { 
    # Allow anybody to upload jpeg's and png's. 
    'imgs': ('uploads/imgs', lambda u: u.is_authenticated(), ['image/jpeg', 'image/png'], 'public-read', AWS_STORAGE_BUCKET_NAME), 
} 
... 

请注意,我们正在使用 AWS_STORAGE_BUCKET_NAME,它应该为 django-storages 配置定义。

在我的情况下稍微复杂一些,因为我为不同的模型使用不同的桶。

只存储 key

当使用 s3-direct 时,一旦用户上传了图片并提交了表单,我们的 View 将收到 S3 放置图片的 url。如果我们存储这个 url,当 s3-storages 试图访问图像时,它不会工作,所以我们要做的就是只存储文件的 key 。

文件的键是存储桶内图像的路径。例如,对于 url https://s3-eu-west-1.amazonaws.com/bucket/uploads/imgs/Most-Famous-Felines-034.jpg key 是 uploads/imgs/Most-Famous-Felines-034.jpg 因此这是我们需要存储在模型中的值。在我的例子中,我使用这个片段从 url 中提取 key :

def key_from_url(url, bucket): 
    try: 
        indexOf = url.index(bucket) 
        return url[indexOf:] 
    except: 
        raise ValueError('The url provided does not match the bucket name') 

在我进行了这些更改后,它就可以无缝运行了。 我希望这可以帮助处于相同情况的任何人。