Repository: jschneier/django-storages

Issue:
[gcloud] set the correct Content-Type for file
gcloud backend uploads the file with default Content-Type so it is set to `application/octet-stream` on Google Cloud Storage.
Use this API https://googlecloudplatform.github.io/google-cloud-python/stable/storage-blobs.html#google.cloud.storage.blob.Blob.content_type to set the correct type (at least for `.css` and `.js` files).


Discussion:
@Sgiath I'll create a pull request for this in one/two days.
@faxioman how it looks like with the Pull Request?

Resolving patch:
diff --git a/storages/backends/gcloud.py b/storages/backends/gcloud.py
--- a/storages/backends/gcloud.py
+++ b/storages/backends/gcloud.py
@@ -1,3 +1,4 @@
+import mimetypes
 from tempfile import SpooledTemporaryFile
 
 from django.core.exceptions import ImproperlyConfigured
@@ -21,6 +22,7 @@
 class GoogleCloudFile(File):
     def __init__(self, name, mode, storage):
         self.name = name
+        self.mime_type = mimetypes.guess_type(name)[0]
         self._mode = mode
         self._storage = storage
         self.blob = storage.bucket.get_blob(name)
@@ -70,7 +72,7 @@ def close(self):
         if self._file is not None:
             if self._is_dirty:
                 self.file.seek(0)
-                self.blob.upload_from_file(self.file)
+                self.blob.upload_from_file(self.file, content_type=self.mime_type)
             self._file.close()
             self._file = None
 
@@ -154,7 +156,8 @@ def _save(self, name, content):
         content.name = cleaned_name
         encoded_name = self._encode_name(name)
         file = GoogleCloudFile(encoded_name, 'rw', self)
-        file.blob.upload_from_file(content, size=content.size)
+        file.blob.upload_from_file(content, size=content.size,
+                                   content_type=file.mime_type)
         return cleaned_name
 
     def delete(self, name):


Test patch:
diff --git a/tests/test_gcloud.py b/tests/test_gcloud.py
--- a/tests/test_gcloud.py
+++ b/tests/test_gcloud.py
@@ -6,6 +6,7 @@
     import mock
 
 import datetime
+import mimetypes
 
 from django.core.files.base import ContentFile
 from django.test import TestCase
@@ -90,7 +91,8 @@ def test_open_write(self, MockBlob):
         # File data is not actually written until close(), so do that.
         f.close()
 
-        MockBlob().upload_from_file.assert_called_with(tmpfile)
+        MockBlob().upload_from_file.assert_called_with(
+            tmpfile, content_type=mimetypes.guess_type(self.filename)[0])
 
     def test_save(self):
         data = 'This is some test content.'
@@ -100,7 +102,7 @@ def test_save(self):
 
         self.storage._client.get_bucket.assert_called_with(self.bucket_name)
         self.storage._bucket.get_blob().upload_from_file.assert_called_with(
-            content, size=len(data))
+            content, size=len(data), content_type=mimetypes.guess_type(self.filename)[0])
 
     def test_save2(self):
         data = 'This is some test ủⓝï℅ⅆℇ content.'
@@ -111,7 +113,7 @@ def test_save2(self):
 
         self.storage._client.get_bucket.assert_called_with(self.bucket_name)
         self.storage._bucket.get_blob().upload_from_file.assert_called_with(
-            content, size=len(data))
+            content, size=len(data), content_type=mimetypes.guess_type(filename)[0])
 
     def test_delete(self):
         self.storage.delete(self.filename)