使用七牛来存储 Django 博客的图片

瞳人


发布于 Sept. 6, 2016, 7:44 p.m.

3 个评论

Django


使用七牛来存储图片,并利用七牛的 CDN 提高图片载入速度。

七牛云存储

七牛 是很不错的一个云存储提供商,并且有不错的 CDN 加速服务。并且,每个月有10G的免费流量。

迁移图片的起因

因为博客服务器在日本的 linode 机房,而太多的人使用日本机房翻墙,所以在大陆访问博客有时候不是很稳定。尤其是较大的图片等文件载入不稳定,有时候飞快,有时候巨慢。因此打算使用国内的云存储以及 CDN 服务。虽然暂时的效果一般,甚至在一些时候速度比原来使用 linode 更慢,但是还是尝试一下这个好玩的云存储。

相关工具

  1. django-qiniu-storage,用于 django 项目,继承了 django 的 Storage 类,使用起来方便。
  2. qshell 七牛官方的一个方便开发者测试和使用七牛API服务的命令行工具。

操作步骤

七牛云存储设置

打开七牛官网,并注册账号。然后添加对象存储资源,如下图所示。

七牛添加对象存储

选择,绿框标记的对象存储的立即添加,在下一页中填选相关信息,例如这里创建的资源叫做 test,如下图所示。

七牛创建bucket

选择确定创建,完成该资源的创建,并跳转到资源管理界面,如下图所示。

七牛完成bucket添加

图中绿框标记的为测试域名,有一定限制,仅用于测试使用。在真正产品部署过程中,我们需要自定义加速域名,具体操作请看七牛官方文档:自定义域名的绑定流程

同步已有资源

因为我的博客之前使用的是服务器本地存储,即 django 默认的 FileSystemStorage,所以已经在本地有一些资源了。于是,我们首先使用七牛提供的 qshell 工具,将这些资源同步到我们刚刚创建的 bucket 中。

qshell 的 github 主页或者七牛的 qshell 开发者页面 进行下载,然后解压缩,找到对应你版本的可执行文件,例如我这里是 64 位的 linux:

1
2
3
wget http://devtools.qiniu.com/qshell-v1.8.0.zip
unzip qshell-v1.8.0.zip
mv qshell_linux_amd64 qshell

七牛后台的密钥管理获得你的密钥,然后登陆。详见 account 命令文档

1
./qshell account <Your AccessKey> <Your SecretKey>

然后使用,qupload 命令,进行同步。

1
./qshell qupload 50 test.config

其中 50 指定开 50 个线程并行上传,后面 test.config 文件是定义的配置文件,如下为参考配置。具体说明详见 qupload 命令文档

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
{
    "src_dir"       :   "/path/to/media",
    "access_key"    :   "<Your AccessKey>",
    "secret_key"    :   "<Your SecretKey>",
    "bucket"        :   "test",
    "key_prefix"    :   "media/",
    "overwrite"     :   true,
    "check_exists"  :   true,
    "check_hash"    :   true
}

配置 django-qiniu-storage

参考其项目说明即可,但是我这里遇到了后文记录的几个问题,因此我 fork 了原项目,然后修改了一部分。详情查看后文内容。

遇到的问题

I/O operation on closed file

我使用了 django-imagekit 来保存图片,而 django-imagekit 在生成图片的时候会调用到 NamedTemporaryFile。 这个临时文件的特殊性是默认在文件关闭后,会做一些操作,包括删除该文件。

使用的 django-qiniu-storage 中在保存文件之后会关闭该文件,使得 NamedTemporaryFile 的一些后续操作抛出异常。

因此,我暂时暴力的注释掉代码中的 content.close

至于使用 django 本身默认的 FileSystemStorage 没有采用这种临时文件,所以没有报错。

Cannot identify image file

我使用了 django-imagekit 中的 ImageSpecField 来生成缩略图,而其 source 的图片文件是存在七牛上的。因为用 django 本身的 FileSystemStorage 不会出现问题,所以我认为应该是 django-qiniu-storage 当中的问题。

我在 settings.py 中是如下设置的:

1
2
3
MEDIA_URL = ''
MEDIA_ROOT = 'media'
DEFAULT_FILE_STORAGE = 'qiniustorage.backends.QiniuMediaStorage'

关于 QiniuMediaStorageMEDIA_ROOT, django-qiniu-storage 文档 说:文件将存放在bucket/MEDIA_ROOT目录下。这一步没啥问题。

但是我发现,django-qiniu-storage 这个项目中在取七牛存储的文件时,是使用 requests.get 来获取的,而我发现该 requests.get 的这个 QiniuFile 对象的 url 不对。这个 url 当中有一个参数是这个文件的 _name 成员变量生成的。于是我打印出这个变量,发现确实不对,这个变量头部被截断了。参见此处代码

1
self._name = name[len(self._storage.location):].lstrip('/')

QiniuMediaStorage 中将 location 设置成了 MEDIA_ROOT,但是事实上传给 QiniuFile 初始化的参数 name 中间本身不包含这个 location 了,所以这样就导致了 url 的不正确,因此改为如下:

1
self._name = name.lstrip('/')

七牛防盗链设置:空 Referer

正如前文提到,django-qiniu-storage 中是使用 requests.get 来获取文件的,而该代码中没有添加 Referer 的头,所以如果在防盗链设置中,禁止了空 Referer 的请求,那么又将会获取不到图片,导致抛出 Cannot identify image file 的错误。关于空 Referer 的请求,可以参考七牛的文档

解决方法有两种,第一种就是,不设置七牛防盗链,或者设置防盗链但是允许空 Referer 请求。第二种方式,就是修改 django-qiniu-storage 的代码,在 requests.get 处,加上你白名单中的域名的 Referer 头即可。

图片上传速度慢

由于本服务器在海外,所以用七牛默认的上传服务器上传有点慢,粗略地可以自己通过 ping 值来查看。可选的如下所示。

七牛up_host

修改的具体代码为:

1
2
from qiniu import Zone, set_default
set_default(default_zone=Zone('up.qiniug.com', 'up.qiniu.com'))

哎呦, 不错哦!

3 Comments

pan July 18, 2017, 4:46 p.m. | Reply

方法不错,请问博主写博客是用什么编辑器? 我也是用django搭建的博客,然后用markdown来写作,所以图片都是先传到其他图床再用链接的. 虽然我服务器也在境外,但肉身在国内所以图片上传还挺快TAT

瞳人 Aug. 19, 2017, 4:47 a.m. | Reply

不好意思,最近比较忙。博客已经很久没更新了。
我没有用什么编辑器。就是用的普通的 textarea,然后用ajax传给服务器来render最后html的效果。
然后图片上传我是用的浏览器直接向七牛的异步传输,我已经不用这篇文章中的方法了。因为本文中是浏览器先将文件传输到境外VPS,然后境外VPS再传输到七牛。这样太慢,容易导致timeout。

Hopetree Oct. 26, 2017, 1:32 a.m. | Reply

博主,你为什么要用境外的那么贵的服务器啊
阿里云不是挺好的么,速度还行啊,除了需要备案这个大坑意外(其实备案时间还算快的)


Leave a Comment:

博客搜索

友情链接

公告

本博客代码已经公布在 Github 上,欢迎交流指正。

QQ 邮箱对 mailgun 不太友好, 所以使用 QQ 邮箱的评论, 可能会无法及时收到邮件。我会尽快寻找其他解决方案的。

本人现在独自使用 linode vps, 20 美元/月, 感觉压力大, 如果有意一起合租, 可以联系我. 在我的任意一篇文章下面留言即可. 关于使用方式, 现在倾向于使用 docker.