天天看點

部落格優化:部落格文章圖檔Django使用ckeditor上傳到七牛雲

因為部落格用的富文本編輯器, 文章部落格圖檔想通過第三方存儲, 這樣可以通過cdn擷取以加速網站打開速度, 搜尋部分資料發現這個篇文章不錯

介紹

最近正在使用Django開發一個項目,有一個需求是需要在背景,使用富文本編輯器,去自定義一些内容。會涉及到圖檔的上傳。我準備把上傳的内容存儲到七牛裡面,不想放在伺服器上面。碰到一些問題,總結一下。

分析

我使用的django-ckeditor版本為5.6.1。前面操作都是跟官網說的一樣。

  • 在setting.py裡面的INSTALLED_APPS增加:
'ckeditor',  # 富文本編輯器
'ckeditor_uploader'  # 富文本編輯器上傳圖檔子產品      

增加upload_path

CKEDITOR_UPLOAD_PATH = "uploads/"      
  • 在model裡面對相應的字段,使用ckeditor_uploader。
from ckeditor_uploader.fields import RichTextUploadingField
content = RichTextUploadingField(verbose_name='内容')      
  • 重點來了,那我怎麼拿到我上傳的檔案或圖檔呢,比如我要傳到七牛。通過檢視ckeditor_uploader的源碼,我們發現有一個路由配置檔案ckeditor_uploader/urls.py。
部落格優化:部落格文章圖檔Django使用ckeditor上傳到七牛雲

很好奇的檢視upload做了什麼事

部落格優化:部落格文章圖檔Django使用ckeditor上傳到七牛雲

大概的閱讀了一下,發現這個地方就是處理使用者上傳檔案的時候,其中儲存檔案是在self._save_file(request, uploaded_file)這個方法裡面操作的。繼續往下面看

部落格優化:部落格文章圖檔Django使用ckeditor上傳到七牛雲

看到這個storage就有點熟悉了,django也有提供,難道是一個東西?

部落格優化:部落格文章圖檔Django使用ckeditor上傳到七牛雲

到這邊就已經很明白了,如果我們不在setting裡面定義storage的話,就預設用的django的storage。

方案

分析完代碼之後,我大概理了一下思路,我想了想一共有兩種方法,可以去解決:

  1. 在setting裡面配置CKEDITOR_STORAGE_BACKEND

這個是比較簡單的,也是官方推薦我們用的方法,我們隻需要處理上傳的地方就可以了。隻需要寫個class把django.core.files.storage封裝一下就可以。

from django.core.files.storage import Storage


class StorageObject(Storage):
    def __init__(self):
        self.now = datetime.datetime.now()
        self.file = None

    def _new_name(self, name):
        new_name = "file/{0}/{1}.{2}".format(self.now.strftime("%Y/%m/%d"), str(uuid.uuid4()).replace('-', ''),
                                             name.split(".").pop())
        return new_name

    def _open(self, name, mode):
        return self.file

    def _save(self, name, content):
        """
        上傳檔案到七牛
        """
        # 建構鑒權對象
        q = Auth(access_key, secret_key)
        token = q.upload_token(bucket_name)
        self.file = content
        file_data = content.file
        ret, info = put_data(token, self._new_name(name), file_data.read())

        if info.status_code == 200:
            base_url = 'http://%s/%s' % (QINIU_URL_DOMAIN, ret.get("key"))
            # 表示上傳成功, 傳回檔案名
            return base_url
        else:
            # 上傳失敗
            raise Exception("上傳七牛失敗")

    def exists(self, name):
        # 驗證檔案是否存在,因為我這邊會去生成一個新的名字去存儲到七牛,是以沒有必要驗證
        return False

    def url(self, name):
        # 上傳完之後,已經傳回的是全路徑了
        return name      

然後在setting裡面配置

CKEDITOR_STORAGE_BACKEND = 'common.storage.StorageObject'      

就可以了。

2. 第二個方法就比較抽象了,在寫url的時候,直接把ckeditor/upload用我們自己的方法來代替。

urlpatterns = [
    path('ckeditor/upload/', staff_member_required(csrf_exempt(CkeditorView.as_view())), name='ckeditor_upload'),
]      

然後寫一個view

class CkeditorView(generic.View):
    permission_classes = [IsAuthenticated]
    http_method_names = ['post']

    def post(self, request, **kwargs):
        uploaded_file = request.FILES['upload']

        backend = image_processing.get_backend()

        ck_func_num = request.GET.get('CKEditorFuncNum')
        if ck_func_num:
            ck_func_num = escape(ck_func_num)

        # Throws an error when an non-image file are uploaded.
        if not getattr(settings, 'CKEDITOR_ALLOW_NONIMAGE_FILES', True):
            try:
                backend.image_verify(uploaded_file)
            except utils.NotAnImageException:
                return HttpResponse("""
                    <script type='text/javascript'>
                    window.parent.CKEDITOR.tools.callFunction({0}, '', 'Invalid file type.');
                    </script>""".format(ck_func_num))
        # 存儲檔案
        url = storage(uploaded_file)

        if ck_func_num:
            # Respond with Javascript sending ckeditor upload url.
            return HttpResponse("""
            <script type='text/javascript'>
                window.parent.CKEDITOR.tools.callFunction({0}, '{1}');
            </script>""".format(ck_func_num, url))
        else:
            retdata = {'url': url, 'uploaded': '1',
                       'fileName': uploaded_file.name}
            return JsonResponse(retdata)      

這樣的話,就可以了。

這個方法不太建議,一般我們沒有必要去修改整個post的過程,隻需要動到upload file這個過程就可以了。

結論