How do you set “Content-Type” when saving to S3 us

2020-08-10 16:38发布

问题:

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?

回答1:

If you go through the source code in class S3BotoStorageFile and function write, the header is updated from only 2 places,

  1. upload_headers.update(self._storage.headers) where self._storage.headers is taken from AWS_HEADERS
  2. 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.



回答2:

According to this answer, the Content-Type isn't metadata but rather headers that you set up when you upload the file.



回答3:

Now you can use django-storages >= 1.4 and it automatically guesses the mime types.



回答4:

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!