在django中使用ImageField将图片存储在bcs中

Posted on 五 05 十二月 2014 in 日常

因为像bae一类的paas平台,一般都是需要将除了代码外的文件存储到其他专门的服务中。为了使本博能上传图片,就使用了bcs,在django中,ImageField默认时存储到磁盘中,为了能上传到云上,需要自己定制storage。

参考python-django如何在sae中使用自带imagefield和filefield

定制Storage

bcs的API使用官网提供的封装好的pybcs,其他就是继承并重载FileSystemStorage。

#-*- coding: UTF-8 -*-
from django.core.files.storage import FileSystemStorage
from django.core.exceptions import SuspiciousOperation
import pybcs
import logging
import uuid
import os

pybcs.init_logging(logging.INFO)
AK = 'your AK
SK = 'your SK'
BUCKET='your bucket'
bcs = pybcs.BCS('http://bcs.duapp.com/', AK, SK, pybcs.HttplibHTTPC)
bckt = bcs.bucket(BUCKET)

class BcsStorage(FileSystemStorage):

    def __init__(self, location=None, base_url=None):
        super(BcsStorage, self).__init__(location, base_url)

    @property
    def maxSize(self):
        """ max file size 2G """
        return 2 * 1024 * 1024 * 1024

    @property
    def fileTypes(self):
        """ the file types allowed """
        return '*'

    def makename(self, name):
        oname = os.path.basename(name)
        path = os.path.dirname(name)
        try:
            fname, hk = oname.split(".")
        except Except, e:
            fname, hk = oname, ''
        if hk:
            rname = "%s_%s.%s" % (str(uuid.uuid4()), fname, hk)
        else:
            rname = "%s_%s" % (str(uuid.uuid4()), fname)
        name = os.path.join(path, rname)
        return name

    def _save(self, name, content):
        hz = name.split(".")[-1]
        if self.fileTypes != '*':
            if hz.lower() not in self.fileTypes:
                print u"不支持的文件类型%s,仅支持%s" % (hz, self.fileTypes)
                raise SuspiciousOperation(u"不支持的文件类型%s,仅支持%s" % (hz, self.fileTypes))
        name = self.makename(name)
        if content.size > self.maxSize:
            raise SuspiciousOperation(u"文件大小超过限制")
        # object name must start with '/'
        if name.startswith('/'):
            o = bckt.object(name)
        else:
            o = bckt.object("/" + name)
        o.put(content)
        #let the object can be read by public
        o.make_public()
        return name

    def delete(self, name):
        # object name must start with '/'
        if name.startswith('/'):
            o = bckt.object(name)
        else:
            o = bckt.object("/" + name)
        o.delete()

class ImageStorage(BcsStorage):

    @property
    def fileTypes(self):
        return ['jpg', 'jpeg', 'png', 'gif']

修改pybcs

印象中应该只有一处有错,在common.py中需要修改shorten函数:

def shorten(s, l=80):
    if len(s)<=l:
        return s
    if hasattr(s, '__getitem__'):
        return s[:l-3] + '...'
    else:
        return 'data...'

使用方法

在定义model时,使用ImageField,并带上参数storage=ImageStorage()。同时需要重载delete,否则不会删除图片。

class PaperImage(models.Model): image = models.ImageField(verbose_name=ugettext(u'图片'), max_length=250, upload_to=UPLOAD_TO, storage=ImageStorage(), null=True, blank=True)

def __unicode__(self):
    return u"%s" % self.image

def delete(self, using=None):
    try:
        self.image.storage.delete(self.image.name)
    except Exception, e:
        print e
        pass
    super(PaperImage, self).delete(using=None)

修改require,txt

我使用的bae,环境中缺少PIL包,在require.txt添加一行,写上PIL