Python 为什么带有S3Boto后端的django存储的默认_storate.exists()会导致S3存储桶较大时出现内存错误?

Python 为什么带有S3Boto后端的django存储的默认_storate.exists()会导致S3存储桶较大时出现内存错误?,python,django,boto,sorl-thumbnail,django-storage,Python,Django,Boto,Sorl Thumbnail,Django Storage,当运行default\u storage.exists() 下面是我的文档: 以下是我的设置文件的相关部分: DEFAULT_FILE_STORAGE = 'storages.backends.s3boto.S3BotoStorage' 下面是我要重复的问题: ./manage.py shell from django.core.files.storage import default_storage # Check default storage is right default_st

当运行
default\u storage.exists()

下面是我的文档:

以下是我的设置文件的相关部分:

DEFAULT_FILE_STORAGE = 'storages.backends.s3boto.S3BotoStorage'
下面是我要重复的问题:

./manage.py shell

from django.core.files.storage import default_storage

# Check default storage is right
default_storage.connection
>>> S3Connection:s3.amazonaws.com

# Check I can write to a file
file = default_storage.open('storage_test_2014', 'w')
file.write("does this work?")
file.close()
file2 = default_storage.open('storage_test_2014', 'r')
file2.read()
>>> 'does this work?'

# Run the exists command
default_storage.exists("asdfjkl") # This file doesn't exist - but the same thing happens no matter what I put here - even if I put 'storage_test_2014'

# Memory usage of the python process creeps up over the next 45 seconds, until it nears 100%
# iPython shell then crashes
>>> Killed
我想到的唯一一个潜在问题是,我的S3存储桶中有93000个条目——我想知道。exists是否只是下载整个文件列表以便检查?如果是这样的话,肯定还有别的办法吗?不幸的是,sorl缩略图在生成新缩略图时使用了这个.exists()函数,这会导致缩略图生成极其缓慢。

更新(2017年1月23日) 为了避免这种情况,您只需在创建
存储时传递
preload\u metadata=False
,或在设置中设置
AWS\u preload\u metadata=False

感谢@r3mot在评论中的建议

原始答案 事实上,这是因为
s3botostrage.exists
调用了
s3botostrage.entries
,如下所示:

    @property
    def entries(self):
        """
        Get the locally cached files for the bucket.
        """
        if self.preload_metadata and not self._entries:
            self._entries = dict((self._decode_name(entry.key), entry)
                                for entry in self.bucket.list(prefix=self.location))
from storages.backends.s3boto import S3BotoStorage, parse_ts_extended


class MyS3BotoStorage(S3BotoStorage):
    def exists(self, name):
        name = self._normalize_name(self._clean_name(name))
        k = self.bucket.new_key(self._encode_name(name))
        return k.exists()

    def size(self, name):
        name = self._normalize_name(self._clean_name(name))
        return self.bucket.get_key(self._encode_name(name)).size

    def modified_time(self, name):
        name = self._normalize_name(self._clean_name(name))
        k = self.bucket.get_key(self._encode_name(name))
        return parse_ts_extended(k.last_modified)
处理这种情况的最佳方法是将
s3botostrage
子类化,如下所示:

    @property
    def entries(self):
        """
        Get the locally cached files for the bucket.
        """
        if self.preload_metadata and not self._entries:
            self._entries = dict((self._decode_name(entry.key), entry)
                                for entry in self.bucket.list(prefix=self.location))
from storages.backends.s3boto import S3BotoStorage, parse_ts_extended


class MyS3BotoStorage(S3BotoStorage):
    def exists(self, name):
        name = self._normalize_name(self._clean_name(name))
        k = self.bucket.new_key(self._encode_name(name))
        return k.exists()

    def size(self, name):
        name = self._normalize_name(self._clean_name(name))
        return self.bucket.get_key(self._encode_name(name)).size

    def modified_time(self, name):
        name = self._normalize_name(self._clean_name(name))
        k = self.bucket.get_key(self._encode_name(name))
        return parse_ts_extended(k.last_modified)

您只需将此子类放入应用程序的一个模块中,并通过设置模块中的虚线路径引用它。这个子类的唯一缺点是,每次调用3个重写方法中的任何一个都会导致一个web请求,这可能不是什么大问题。

@AndrewWidgery没有问题-顺便说一下,我刚刚更新了
size
方法,从
返回
行的size属性访问中删除
()
(这是文件的属性,而不是方法)。这可能是一个例外。您也可以在创建存储时传递
preload\u metadata=False
,或者在django设置中设置
AWS\u preload\u metadata=False
file@r3m0t哇,太棒了,谢谢你的提示-增加了答案!