简体   繁体   中英

How do you set "Content-Type" when saving to S3 using django-storages with S3boto backend?

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,

  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.

根据此答案, 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:

  1. Implement custom file manager:
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

  1. Use ManagedS3BotoS3Storage in model:
class SomeCoolModel(models.Model):
    file = models.FileField(
        storage=ManagedS3BotoS3Storage(bucket="my-awesome-S3-bucket"),
        upload_to="my_great_path_to_file",
    )
  1. Run 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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM