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

3πŸ‘

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.

πŸ‘€NEB

2πŸ‘

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

πŸ‘€Brad Pitcher

2πŸ‘

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!

1πŸ‘

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

πŸ‘€Mondongo

0πŸ‘

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

πŸ‘€Maciej M

Leave a comment