I am using django-storages
with s3boto
as a backend.
I have one bucket with two folders - one for static
and one for media
. I achieve this using django-s3-folder-storage
.
As well as saving to S3 using a model, I also want to implement an image-resize-and-cache function to save the files to S3. To do this I interact directly with my S3 bucket. The code works, but the Content-Type
isn't set on S3.
in iPython:
In [2]: from s3_folder_storage.s3 import DefaultStorage
In [3]: s3media = DefaultStorage()
In [4]: s3media
Out[4]: <s3_folder_storage.s3.DefaultStorage at 0x4788780>
Test we're accessing the right bucket - storage_test
is one I created earlier:
In [5]: s3media.exists('storage_test')
Out[5]: True
In [6]: s3media.open("test.txt", "w")
Out[6]: <S3BotoStorageFile: test.txt>
In [7]: test = s3media.open("test.txt", "w")
In [8]: test
Out[8]: <S3BotoStorageFile: test.txt>
In [9]: test.key.content_type = "text/plain"
In [10]: test.write("...")
In [11]: test.close()
In [12]: test = s3media.open("test.txt", "w")
In [13]: test.key.content_type
Out[13]: 'binary/octet-stream'
I've also tried instead of In [9]
using test.key.metadata
and test.key.set_metadata
. None of them do it.
How do I set the correct Content-Type?
If you go through the source code in class S3BotoStorageFile
and function write
, the header is updated from only 2 places,
upload_headers.update(self._storage.headers)
where self._storage.headers
is taken from AWS_HEADERS
self._storage.default_acl
And in function _flush_write_buffer
only self._storage.headers
is considered. Check for the line headers = self._storage.headers.copy()
So updating test.key.content_type
will not work.
Instead of test.key.content_type = "text/plain"
at In [9]:
try using test._storage.headers['Content-Type'] = 'text/plain'
, it should work.
根据此答案, Content-Type 不是元数据,而是您在上传文件时设置的标头。
现在您可以使用django-storages >= 1.4
并且它会自动猜测 mime 类型。
This is for Boto3 ONLY, not Boto. If you would like to set those headers, you will need to access the object like so, file_ is refereing to a FileField with storage setup to be using Boto3 from django-storages:
file_.storage.object_parameters = { 'ContentType': 'text/plain' }
NOTE: it requires header names to be camelcase, so Content-Type
= ContentType
, Content-Dispostion
= ContentDispostion
etc. Hope this helps!
I've had a similar issue - I wanted to set my header for all the files uploaded to S3 using django-storages
, without relying on the default library approach which is guessing mime type based on the filename.
Please note that you can tweak the way how the header is set and you don't have to have it fixed like I have (my case was specific).
This is what worked for me:
import os
from storages.backends.s3boto3 import S3Boto3Storage
class ManagedS3BotoS3Storage(S3Boto3Storage):
def _save(self, name, content):
cleaned_name = self._clean_name(name)
name = self._normalize_name(cleaned_name)
params = self._get_write_parameters(name, content)
content_type = "application/octet-stream". # Content-Type that I wanted to have for each file
params["ContentType"] = content_type
encoded_name = self._encode_name(name)
obj = self.bucket.Object(encoded_name)
if self.preload_metadata:
self._entries[encoded_name] = obj
content.seek(0, os.SEEK_SET)
obj.upload_fileobj(content, ExtraArgs=params)
return cleaned_name
ManagedS3BotoS3Storage
in model:class SomeCoolModel(models.Model):
file = models.FileField(
storage=ManagedS3BotoS3Storage(bucket="my-awesome-S3-bucket"),
upload_to="my_great_path_to_file",
)
python manage.py makemigrations
. That's it, after this all files that I had was uploaded with Content-Type: "application/octet-stream
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.