Django Heroku S3 django-storages のファイルpathについて
参照サイト
File storage API | Django documentation | Django
django+heroku+s3で本番環境を整えていたところ、ファイルアップ関連で以下のエラーが出てしまった。
raise NotImplementedError("This backend doesn't support absolute paths.") NotImplementedError: This backend doesn't support absolute paths.
これはどうも、django-storageと、デフォのstorageが競合しているらしい。下の記事に書いてあった。
Solving django-storage NotImplementedError with Amazon S3 | Hardly Code Blog
The conflict was in the s3boto storage class the path method was not implemented. As per the Django documentation, for non local storage you should not implement this method.
とうも、ドキュメントによると、non localな環境だったら、デフォのstorageは使わないほうがいいらしいということ。
The only way I found to solve this solution was to go ahead and implement this method in the django-storage code and modify storages.backends.s3boto and add the path method.
ただ、この方法はややレベル高めな気がするので、以下の方法を試してみたい。
path -> nameに変える
Resize thumbnails django Heroku, 'backend doesn't support absolute paths' - Stack Overflow
image.save(self.project_thumbnail.name)
元々のコード例:
image.save(self.project_thumbnail.path)
うーん、全くうまくいかない。ただ、さっきの、NotImplementedError
は解消された。
新たにsuch a file does not exist
みたいなerrorが出たので、多分path関連の設定がlocalとはかなり違ってくる模様。
再度原因を考えてみる。
以下がエラーが出ているコードの概要
def create_image(media_path, instance, owner_slug): filename = os.path.basename(media_path) basename, file_extension = filename.split(".") thumb = Image.open(media_path) gray_img = thumb.convert('L') temp_loc = "%s/%s/tmp" %(settings.MEDIA_ROOT, owner_slug) if not os.path.exists(temp_loc): os.makedirs(temp_loc) temp_file_path = os.path.join(temp_loc, filename) if os.path.exists(temp_file_path): temp_path = os.path.join(temp_loc, "%s" %(random.random())) os.makedirs(temp_path) temp_file_path = os.path.join(temp_path, filename) temp_image = open(temp_file_path, "w") gray_img.save(temp_image,'JPEG')# そのうちちょっと変える。 thumb_data = open(temp_file_path, "rb") thumb_file = File(thumb_data) instance.image.save(filename, thumb_file) instance.save() def image_original_post_save_receiver(sender, instance, *args, **kwargs): if instance.image: obj = Image.objects.all().filter(original__id=instance.id) print(obj) if obj: obj.first().delete() processed_obj = MonochromeProcessed.objects.create(original=instance) owner_slug = instance.slug media_path = instance.image.path create_monochrome_image(media_path, processed_obj, owner_slug)
保存した画像を、post_saveのタイミングで、別のデータベースにグレースケールに変換して保存するというコード。
ローカルだとうまくいくけど、クラウドストレージだとpath関連をうまく設定できずに、ファイル参照に失敗して、errorが出てしまうという状態。
nameと、storage.pathを使ってみる。
Solving django-storage NotImplementedError with Amazon S3 | Hardly Code Blog
ここのサイトのコメント欄に有益ぽい情報を発見!以下のようにすると、pathが通るかもとのこと!
Instead of: photo = Image.open(self.image.path) Use: from django.core.files.storage import default_storage as storage photo = Image.open(storage.open(self.image.name))
ポイント
- default_storageをstorageとしてimportする
- image.name としている点(pathだとerrorが出る。絶対pathはlocalのみと覚えておこう)
これでとりあえず、NotImplementedErrorと、pathが変で、開けないというerrorは解消された!
新たなerror OSError: [Errno 30] Read-only file system: ‘/static_in_env’
が出てきた。これはおそらく、os.makedir(temp_loc)の箇所で出てきているerror。
新たなerror OSError: [Errno 30] Read-only file system: ‘/static_in_env’
について考えてみる。
どうも、そもそも、S3にはフォルダとかディレクトリという概念が無い模様。 そのため、単純にnameを与えれば、それに従ってフォルダとかぽいものは作ってくれるということか??
Amazon S3 boto - how to create a folder? - Stack Overflow
settings.MEDIA_ROOT -> settings.MEDIA_URL で一発で問題解決!
MEDIA_ROOTをsettingsでセットしてなかったので、それをsettings.MEDIA_URLに変更したところ一発でerrorが出なくなった!
temp_loc = "%s/%s/tmp" %(settings.MEDIA_ROOT, owner_slug)
これを以下のように変更
temp_loc = "%s/%s/tmp" %(settings.MEDIA_URL, owner_slug)
これで、S3のbucketに無事リンクされて保存された!