工作中应用图片时会觉得图片能压缩又不失去显示效果,岂不美哉,果断用 tinify 来压缩图片,一来可以减小图片占用空间大小,二来放到服务器后还能节省存储空间,用于显示时也能提高图片的访问速度。
之前已经在 django 博客项目中使用 django-qiniu-storage 做过 从 ckeditor 把图片上传到七牛云。
目前又闪出一个想法,在 django 中实现:图片先压缩再上传到七牛云。压缩图片使用 tinify(使用了 tinify 之后多了一套文件上传和下载过程,可能会造成上传一张图片的速度比之前慢上许多,也可能因网络问题造成失败)
前端上传的图片到 Django 时,把图片传给 tinify, tinify 返回图片,再把 返回的图片传给 七牛云,得到图片链接。
# ckeditor_storage.py
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
@author: yinzhuoqun
@site: http://zhuoqun.info/
@email: yin@zhuoqun.info
@time: 2019/7/9 16:57
"""
import datetime
import uuid
import tinify
from django.core.files.storage import Storage
from qiniu import Auth, put_data
from joyoo.settings import QINIU_ACCESS_KEY, QINIU_SECRET_KEY, QINIU_BUCKET_DOMAIN_FILE, QINIU_BUCKET_NAME_FILE, \
    TINIFY_ON, TINIFY_KEY, TINIFY_KEY_USE_MAX
from logger.logger import logger
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 _to_tinify(self, content):
        if self.file.name.lower().endswith(("jpg", "png")) and TINIFY_ON:
            tinify.key = TINIFY_KEY
            try:
                tinify.validate()
            except Exception as e:
                # Validation of API key failed.
                pass
            compressions_this_month = tinify.compression_count
            logger.info("提示:tinify 本月压缩次数已使用 %s/500" % compressions_this_month)
            if compressions_this_month < TINIFY_KEY_USE_MAX:
                try:
                    file_data = tinify.from_file(content.file).to_buffer()
                except Exception as e:
                    logger.error(e)
                    file_data = content.file.read()
            else:
                file_data = content.file.read()
        else:
            file_data = content.file.read()
        return file_data
    def _save(self, name, content):
        """
        上传文件到七牛
        """
        # 构建鉴权对象
        q = Auth(QINIU_ACCESS_KEY, QINIU_SECRET_KEY)
        token = q.upload_token(QINIU_BUCKET_NAME_FILE)
        self.file = content
        # file_data = content.file.read()
        file_data = self._to_tinify(content)
        ret, info = put_data(token, self._new_name(name), file_data)
        if info.status_code == 200:
            base_url = 'http://%s/%s' % (QINIU_BUCKET_DOMAIN_FILE, ret.get("key"))
            # 表示上传成功, 返回文件名
            return base_url
        else:
            # 上传失败
            raise Exception("上传七牛失败")
    def exists(self, name):
        # 验证文件是否存在,因为我这边会去生成一个新的名字去存储到七牛,所以没有必要验证
        return False
    def url(self, name):
        # 上传完之后,已经返回的是全路径了
        return name
# settings.py
# tinify.key 图片压缩配置
TINIFY_ON = True  # 压缩开关
TINIFY_KEY = "xxxxxxxZC"  # 免费版每月 500 张图片
TINIFY_KEY_USE_MAX = 500  # 免费版每月 500 张图片
# 上传过程
[24/Sep/2019 22:35:26] "GET /static/images/favicon.ico HTTP/1.1" 200 1150
11 <class 'django.core.files.uploadedfile.InMemoryUploadedFile'>
22 <class 'django.core.files.uploadedfile.InMemoryUploadedFile'>
提示:tinify 压缩次数本月已使用 2/500
[24/Sep/2019 22:35:50] "POST /ckeditor/upload/?CKEditor=id_content&CKEditorFuncNum=1&langCode=zh-cn HTTP/1.1" 200 207
# 压缩前后比较
直接改 tinify, django 收到前端传来的图片,把图片传给 tinify, tinify 直接把压缩好的图片传给七牛云,压缩的图片不用回传,tinify 返回图片地址。这种方法,目前还没搞定怎么弄,只是个想法。