图床——基于七牛JS-SDK和KVDB


原文链接:图床——基于七牛JS-SDK和KVDB

浏览
浏览

上传

服务端获取token

# -*- coding: utf-8 -*-
__author__ = 'Sky'

import qiniu
from datetime import datetime
import config


def get_token():
    q = qiniu.Auth(config.QINIU_ACCESS_KEY, config.QINIU_SECRET_KEY)
    # print str(datetime.now())
    token = q.upload_token(config.PIC_BUCKET)
    return token

JS上传到七牛

详细使用可参考七牛JS-SDK

var uploader = Qiniu.uploader({
    runtimes: 'html5,flash,html4',
    browse_button: 'pickfiles',
    container: 'container',
    drop_element: 'container',
    max_file_size: '100mb',
    filters: {
        mime_types: [
            { title: "图片文件", extensions: "jpg,gif,png,bmp" }
        ],
        prevent_duplicates: true //不允许队列中存在重复文件
    },
    flash_swf_url: '/static/js/plupload/Moxie.swf',
    dragdrop: true,
    chunk_size: '4mb',
    uptoken_url: $('#uptoken_url').val(),
    domain: $('#domain').val(),
    auto_start: true,
    init: {
        'FilesAdded': function(up, files) {
            $('table').show();
            $('#success').hide();
            plupload.each(files, function(file) {
                var progress = new FileProgress(file, 'fsUploadProgress');
                progress.setStatus("等待...");
            });
        },
        'BeforeUpload': function(up, file) {
            var progress = new FileProgress(file, 'fsUploadProgress');
            var chunk_size = plupload.parseSize(this.getOption('chunk_size'));
            if (up.runtime === 'html5' && chunk_size) {
                progress.setChunkProgess(chunk_size);
            }
        },
        'UploadProgress': function(up, file) {
            var progress = new FileProgress(file, 'fsUploadProgress');
            var chunk_size = plupload.parseSize(this.getOption('chunk_size'));
            progress.setProgress(file.percent + "%", up.total.bytesPerSec, chunk_size);

        },
        'UploadComplete': function() {
            $('#success').show();
        },
        'FileUploaded': function(up, file, info) {
            var progress = new FileProgress(file, 'fsUploadProgress');
            progress.setComplete(up, info);
            var res = $.parseJSON(info);
            // 请求后台存储
            var domain = up.getOption('domain');
            //url = domain + encodeURI(res.key);
            var link = domain + res.key;
            var key = res.key;
            $.ajax({
                url:"/upQiniu",
                type: "POST",
                data: { key: key, url: link}
            })
            .done(function(data){
                if(data === 'error') {
                    alert('上传失败');
                    location.reload(true);
                }
            });
        },
        'Error': function(up, err, errTip) {
            $('table').show();
            var progress = new FileProgress(err.file, 'fsUploadProgress');
            progress.setError();
            progress.setStatus(errTip);
        }
        ,
        'Key': function(up, file) {
            //当前时间戳
            var key = new Date();
            return key.getTime()
        }
    }
});

保存信息到KVDB

# -*- coding: utf-8 -*-
__author__ = 'Sky'

import sae
import json
import sae.kvdb

class KvdbStorage():
    # 初始化kvdb
    def __init__(self):
        self.kv = sae.kvdb.KVClient()

    # 获取value
    def get_value(self, key):
        return self.kv.get(key)

    # 获取dict_value
    def get(self, key):
        string_value = self.kv.get(key)
        if string_value is None:
            return None
        return decode_dict(string_value)

    # 设置value
    def set_value(self, key, value):
        self.kv.set(key, value)

    # 设置dict_value
    def set(self, key, dict_value):
        string_value = encode_dict(dict_value)
        self.kv.set(key, string_value)

    # 批量获取key
    def getkeys_by_prefix(self, prefix, limit=100, marker=None):
        return list(self.kv.getkeys_by_prefix(prefix, limit=limit, marker=marker))

    # 批量获取key/value
    def get_by_prefix(self, prefix, limit=100, marker=None):
        return self.kv.get_by_prefix(prefix, limit=limit, marker=marker)

    # 删除key
    def delete(self, key):
        self.kv.delete(key)

# 编码字典
def encode_dict(my_dict):
    return "\x1e".join("%s\x1f%s" % x for x in my_dict.iteritems())

# 解码字典
def decode_dict(my_string):
    return dict(x.split("\x1f") for x in my_string.split("\x1e"))
  • 只存储了key和图片url,其他暂时是固定信息,便于以后扩展。
# 上传文件到七牛
@app.route('/upQiniu', methods=['POST', 'GET'])
def upQiniu():
    if request.form.get('key'):
        url = request.form.get('url')
        upkey = request.form.get('key')
        # 时间差,用于加载排序
        import time
        future_time = int(time.mktime(datetime.strptime('3000-01-01 00:00:00.000', "%Y-%m-%d %H:%M:%S.%f").timetuple()) * 1000)
        uid = future_time - int(upkey)
        # 存入DB
        key = 'picbed_%s' % uid
        object = 'Sunset Lake'
        words = 'A peaceful sunset view...'
        author = 'skyway'
        # print key
        data = {'upkey': upkey, 'object': object, 'words': words, 'author': author, 'url': url}
        kv.set(str(key), data)
        # kv.set_value(str(key), url)
        return 'success'

浏览

访问获取key

# 初始加载
key_values = kv.get_by_prefix('picbed_', 20, None)
# print sorted(kv.getkeys_by_prefix('picbed', 3, None))
data = tuple(key_values)
json_data = []
for item in data:
    tmp = {}
    tmp['key'] = item[0]
    if '\x1e' in item[1]:
        content = decode_dict(item[1])
        # print content
        tmp['url'] = content['url'] + "?imageView2/2/w/400/format/jpg"
        tmp['object'] = content['object']
        tmp['words'] = content['words']
        tmp['upkey'] = content['upkey']
        tmp['author'] = content['author']
    else:
        tmp['url'] = item[1] + "?imageView2/2/w/400/format/jpg"
        tmp['object'] = 'Sunset Lake'
        tmp['words'] = 'A peaceful sunset view...'
        tmp['upkey'] = item[1][-13:]
        tmp['author'] = 'skyway'
    json_data.append(tmp)
# data = sorted(data, reverse=True)
# print data
return render_template('PicBed.html', data=json_data, domain=config.PIC_DOMAIN)

瀑布流和数据加载

  • Server端返回json数据
# 加载更多
if request.form.get('key'):
    key = request.form.get('key')
    # print key
    key_values = kv.get_by_prefix('picbed_', 10, key)
    # print sorted(kv.getkeys_by_prefix('picbed', 3, key))
    data = tuple(key_values)
    # data = sorted(data, reverse=True)
    print data
    json_data = []
    for item in data:
        tmp = {}
        tmp['key'] = item[0]
        if '\x1e' in item[1]:
            content = decode_dict(item[1])
            # print content
            tmp['url'] = content['url'] + "?imageView2/2/w/400/format/jpg"
            tmp['object'] = content['object']
            tmp['words'] = content['words']
            tmp['upkey'] = content['upkey']
            tmp['author'] = content['author']
        else:
            tmp['url'] = item[1] + "?imageView2/2/w/400/format/jpg"
            tmp['object'] = 'Sunset Lake'
            tmp['words'] = 'A peaceful sunset view...'
            tmp['upkey'] = item[1][-13:]
            tmp['author'] = 'skyway'
        json_data.append(tmp)
    json_str = json.dumps(json_data)
    return json_str
  • 加载更多和渲染数据:
$(window).scroll(function () {
    var clientHeight = $(window).height(),
        scrollTop = $(window).scrollTop(),
        scrollHeight = $(document).height();
    if (!isScroll && (scrollHeight - clientHeight - scrollTop < 500)) {
        isScroll = 1;
        $('#loading').show();
        // 获取最后一个块的key
        var key = $('.grid:last-child').data('key');
        // 请求数据
        $.ajax({
            url:"/picbed",
            type: "POST",
            data: { key: key}
        })
        .done(function(data){
            if(data === 'error') {
                alert('加载失败');
                location.reload(true);
                return false;
            }
            var pics = JSON.parse(data);
            //没有更多了
            if(jQuery.isEmptyObject(pics)){
                $('#load_img').hide();
                $('#load_text').text('没有更多了!');
                $('#contain').BlocksIt('reload');
                return false;
            }

            $.each(pics, function(index, val){
                var add =
                        "<div class='grid' data-key='" + this.key +"'>" +
                            "<div class='imgholder'>" +
                                "<a class='fopLink' data-key='"+ this.upkey + "'>" +
                                    "<img class='img_load' src='" + this.url +"' />" +
                                "</a>" +
                            "</div>" +
                            "<span class='stop'></span>" +
                            "<strong>"+ this.object +"</strong>" +
                            "<div class='description'>" +
                                "<p>"+ this.words +"</p>" +
                                "<div class='meta'>by " + this.author +"</div>" +
                            "</div>"+
                        "</div>";
                $("#contain").append(add);
                // 动态加载渲染图片
                img_stop();
            });
            $('#contain').BlocksIt('reload');
        });
    }
});
  • 瀑布流效果使用插件Blockits.js
    效果不是很好,会产生重叠

长图渲染

对于过长的图片,影响整个浏览的体验,于是超过一定长度的图片,超出部分隐藏,并在图片尾部添加波浪表示图片未展示完全。

// 渲染图片函数
var img_stop = function(){
    var height = 800;
    $('.img_load').each(function() {
        $(this).load(function(){
            if( $(this).height() > height){
                $(this).parent().parent().next().css('display', 'block');
            }
        });
    });
};

// 初始加载渲染图片
$(function(){
    img_stop();
});

var img_load = function(url, key) {
    var img = new Image();
    img.src = url;
    img.onload = load_img(img, key);
    function load_img(img, key){
        var height = img.height;
        if( height > 500){
            var str = 'div[data-key=' + key + ']';
            $(str).find('.stop').css('display', 'block');
        }
    }
};

效果

  • PC端


    浏览
    浏览

    上传
    上传

    预览
    预览
  • 移动端


    浏览
    浏览
    上传
    上传
    预览
    预览

GitHub源码

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 211,123评论 6 490
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,031评论 2 384
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 156,723评论 0 345
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,357评论 1 283
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,412评论 5 384
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,760评论 1 289
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,904评论 3 405
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,672评论 0 266
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,118评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,456评论 2 325
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,599评论 1 340
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,264评论 4 328
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,857评论 3 312
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,731评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,956评论 1 264
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,286评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,465评论 2 348

推荐阅读更多精彩内容

  • Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智...
    卡卡罗2017阅读 134,628评论 18 139
  • 李丽双阅读 215评论 0 0
  • 每天好心情,每天奇迹不断,今天也奇迹发生,多年末见的朋友让我回一个金融集团的经纪公司上班,正是我想的。交流平和,下...
    童希园阅读 211评论 0 0
  • 闲暇时间,画个画,培养点儿爱好。 先晒一下画画的桌子和我的工具、书。 开始画画,第一张。 真人实习一下,照片就不发...
    Doris_Lee阅读 553评论 6 3
  • 一堆心事而已 不读,堆在眼前 读了,堆进心里 一页一行是千山万水 一字一句是春风秋雨 放下天地,放不下这些心事。 ...
    梅香寒_51c9阅读 204评论 0 1