`
willvvv
  • 浏览: 334203 次
  • 性别: Icon_minigender_1
  • 来自: 深圳
社区版块
存档分类
最新评论

三、简单文件服务器,自动异步缩略图

阅读更多

1.log.conf,python的日志模块,详见:http://www.red-dove.com/python_logging.html

[loggers]
keys=root,upload,resize

[handlers]
keys=consoleHandler,uploadFileHandler,resizeFileHandler

[formatters]
keys=simpleFormatter

[formatter_simpleFormatter]
format=%(asctime)s %(levelname)s %(message)s

[logger_root]
level=DEBUG
handlers=consoleHandler

[logger_upload]
level=DEBUG
handlers=uploadFileHandler
qualname=upload

[logger_resize]
level=DEBUG
handlers=resizeFileHandler
qualname=resize

[handler_consoleHandler]
class=StreamHandler
level=DEBUG
formatter=simpleFormatter
args=(sys.stdout,)

[handler_uploadFileHandler]
class=handlers.TimedRotatingFileHandler
level=DEBUG
formatter=simpleFormatter
args=('upload.log',"midnight", 1)

[handler_resizeFileHandler]
class=handlers.TimedRotatingFileHandler
level=DEBUG
formatter=simpleFormatter
args=('resize.log',"midnight", 1)

 跟java中的log4j很类似,定义三个logger对象:root,upload,resize,其中root是StreamHandler类型,使用sys.out;upload和resize是TimedRotatingFileHandler,每天生成一个日志文件的方式。

 

2.conf.py配置文件

FILE_BIG_PATH = '/data/hiluofilev2/big/'
FILE_SMALL_PATH = '/data/hiluofilev2/small/'
HEAD_BIG_PATH = '/data/hiluohead/big/'
HEAD_SMALL_PATH = '/data/hiluohead/small/'

GEARMAN_SERVER = '127.0.0.1:4730'
RESIZE_WORKER = 'resize'
FILE_RESIZE = '140x160!'
HEAD_RESIZE = '86x86!'
IMAGE_FILE = ['jpg','png','gif','jpeg']
AUDIO_FILE = ['amr']

HTTP_FILE = 'http://192.168.0.181:9999/file/'
HTTP_HEAD = 'http://192.168.0.181:9999/head/'

import logging
import logging.config
import os

logging.config.fileConfig(os.path.join(os.path.dirname(os.path.abspath(__file__)), "log.conf"))

 

FILE_BIG_PATH:这几个PATH表示原图和缩略图保存路径,根据业务区分,用户头像和用户上传的文件分开存放。

GEARMAN_SERVER:gearman Server端的地址。

RESIZE_WORKER:执行图片缩放的worker的名字。

FILE_RESIZE和HEAD_RESIZE:普通图片和头像的缩放尺寸。

IMAGE_FILE和AUDIO_FILE:系统支持的图片格式和音频文件格式,业务上仅支持两种类型。

HTTP_FILE和HTTP_HEAD:文件下载的HTTP前缀。

然后是初始化logging的功能。

 

3.worker_resize.py 生成缩略图的worker

#!/usr/bin/env python

import os
import gearman
import math
import conf
import logging
import pickle
from pgmagick import Image, FilterTypes

log_resize = logging.getLogger("resize")

class CustomGearmanWorker(gearman.GearmanWorker):  
    def on_job_execute(self, current_job):  
        log_resize.info("Resize Worker started") 
        return super(CustomGearmanWorker, self).on_job_execute(current_job)  
 
def resize_task_callback(gearman_worker, job):  
    filein = job.data.split('-')[0]
    fileout = job.data.split('-')[1]
    fileresize = job.data.split('-')[2]
    log_resize.info('start filein:%s -> fileout:%s' % (filein,fileout))
    im = Image(filein)
    im.quality(80)
    im.filterType(FilterTypes.SincFilter)
    im.scale(fileresize)
    im.sharpen(1.0)
    im.write(fileout) 
    log_resize.info('end filein:%s -> fileout:%s' % (filein,fileout))
    return job.data

new_worker = CustomGearmanWorker([conf.GEARMAN_SERVER])  
new_worker.register_task("resize", resize_task_callback)  
new_worker.work()

 

jobdata是string,filein代表原图路径,fileout代表缩略图路径,fileresize代表缩放尺寸

 

4.upload.py  用于上传文件和测试上传文件,使用web.py实现

#!/usr/bin/env python
# coding: utf-8

import web
import conf
import logging
import gearman

urls = (
        '/', 'Index',
        '/upload', 'FileUpload'
       )

app = web.application(urls, globals())
log_upload = logging.getLogger("upload")
# get some directories in conf file
filebigdir = conf.FILE_BIG_PATH 
filesmalldir = conf.FILE_SMALL_PATH 
headbigdir = conf.HEAD_BIG_PATH 
headsmalldir = conf.HEAD_SMALL_PATH 

resize_client = gearman.GearmanClient([conf.GEARMAN_SERVER])

class Index:
    def GET(self):
        web.header("Content-Type","text/html; charset=utf-8")
        return """<html><head></head><body>this hiluofileV2</body></html>"""

class FileUpload:
    def GET(self):
        web.header("Content-Type","text/html; charset=utf-8")
        return """<html><head></head><body>
                <form method="POST" enctype="multipart/form-data" action="">
                username:<input type="text"  name="username" size="20" /><br/>
                md5 or sha1:<input type="text"  name="md5orsha1" size="80"/><br/>
                <input type="file" name="myfile" />
                <br/>
                <input type="submit" />
                </form>
                </body></html>"""

    def POST(self):
        client = web.ctx.environ['REMOTE_ADDR']
        x = web.input(myfile={},username="",md5orsha1="")
        username = x.username.encode('utf-8')
        intusername = 0
        try:
            intusername = int(username)                                             # check if the username is right
        except ValueError:
            log_upload.error('ip:%s username:%s username not right!' % (client,username))
            return 'error[username]'
        md5orsha1 = x.md5orsha1.encode('utf-8')                                     # check if the md5orsha1 is right
        if len(md5orsha1)==32 or len(md5orsha1)==40:
            if 'myfile' in x:                                                       # check if the file-object is created
                filepath = x.myfile.filename.replace('\\','/')
                filename = filepath.split('/')[-1]
                filesuffix = filename.split('.')[-1].lower()
                filename = md5orsha1.lower() + '.' + filesuffix                     # get saved filename(md5orsha1+‘.’+suffix)
                filebig = filebigdir + filename
                if filesuffix in conf.AUDIO_FILE:                                   # audio file just save no need to resize
                    fout = open(filebig,'w')                                        # creates the file where the uploaded file should be stored
                    fout.write(x.myfile.file.read())                                # writes the uploaded file to the newly created file.
                    fout.close()                                                    # closes the file, upload complete.
                    log_upload.info('ip:%s username: %d upload: %s ok!' % (client,intusername,filename))
                    fileurl = conf.HTTP_FILE + 'big/' + filename                    # file download url
                    return fileurl
                elif filesuffix in conf.IMAGE_FILE:
                    filesmall = filesmalldir + filename                             # thumbnail file
                    fileresize = conf.FILE_RESIZE                                   # default FILE_RESIZE
                    fileurl = conf.HTTP_FILE + 'small/' + filename
                    if len(md5orsha1)==40:                                          # user upload head
                        filebig = headbigdir + filename
                        filesmall = headsmalldir + filename                         # thumbnail head
                        fileresize = conf.HEAD_RESIZE                               # thumbnail head HEAD_RESIZE
                        fileurl = conf.HTTP_HEAD + 'small/' + filename
                    fout = open(filebig,'w')                                        # creates the file where the uploaded file should be stored
                    fout.write(x.myfile.file.read())                                # writes the uploaded file to the newly created file.
                    fout.close()                                                    # closes the file, upload complete.
                    log_upload.info('ip:%s username:%d upload:%s ok!' % (client,intusername,filename))
                    jobdata = ('%s-%s-%s' % (filebig,filesmall,fileresize))
                    resize_client.submit_job(conf.RESIZE_WORKER, jobdata)           # submit_job resize and ignore job result
                    log_upload.info('submit resize:%s ok!' % (jobdata))
                    return fileurl
                else:
                    log_upload.error('ip:%s username:%d upload:%s unsupport file type' % (client,intusername,filename))
                    return 'error[filetype]'
            else:
                log_upload.error('ip:%s username:%d upload no myfile' % (client,intusername))
                return 'error[myfile]'
        else:
            log_upload.error('ip:%s username:%d upload:%s unsupport md5orsha1' % (client,intusername,md5orsha1))
            return 'error[filemd5orsha1]'


if __name__ == "__main__":
    #app = web.application(urls, globals())
    web.wsgi.runwsgi = lambda func, addr=None: web.wsgi.runfcgi(func, addr)
    app.run()
 

这里业务上定义传头像的key是40位的sha1编码,普通图像文件是32位的md5编码。

 

5.nginx的nginx.conf修改,使用fastCGI方式运行,

在http节点下增加:

proxy_redirect          off;
proxy_set_header        Host $host;
proxy_set_header        X-Real-IP $remote_addr; #获取真实IP
client_max_body_size    10m;
client_body_buffer_size 128k;
proxy_connect_timeout   90;
proxy_send_timeout      90;
proxy_read_timeout      90;
proxy_buffer_size       4k;
proxy_buffers           4 32k;
proxy_busy_buffers_size 64k;
proxy_temp_file_write_size 64k;

增加fastCGI静态图片的下载location,nginx开启8099端口代理fastCGI,9999端口用来下载文件

 

server{
        listen       8099;
        server_name  fast;
        charset      utf-8;

        location / {
            fastcgi_param REQUEST_METHOD $request_method;
            fastcgi_param QUERY_STRING $query_string;
            fastcgi_param CONTENT_TYPE $content_type;
            fastcgi_param CONTENT_LENGTH $content_length;
            fastcgi_param GATEWAY_INTERFACE CGI/1.1;
            fastcgi_param SERVER_SOFTWARE nginx/$nginx_version;
            fastcgi_param REMOTE_ADDR $remote_addr;
            fastcgi_param REMOTE_PORT $remote_port;
            fastcgi_param SERVER_ADDR $server_addr;
            fastcgi_param SERVER_PORT $server_port;
            fastcgi_param SERVER_NAME $server_name;
            fastcgi_param SERVER_PROTOCOL $server_protocol;
            fastcgi_param SCRIPT_FILENAME $fastcgi_script_name;
            fastcgi_param PATH_INFO $fastcgi_script_name;
            fastcgi_pass 127.0.0.1:8091;
        }
    }

    server
    {
        listen       9999;
        server_name  file.hiluo.cn;

        location /file/ {
            alias  /data/hiluofilev2/;
        }    

        location /head/ {
            alias  /data/hiluohead/;
        }    
     }

 

注意,在配置nginx的fastcgi时可能会报错如下:

5.1.child exited with 2
解决方法: insert #!/usr/bin/env python into header of main.py
5.2.spawn-fcgi child exited with 126
解决方法: chmod +x upload.py

5.3.child exited with1

这时可以先修改upload.py,屏蔽web.wsgi.runwsgi行,打开app = web.application(urls, globals())行,用python upload.py 8091方式启动测试。

 

6.hiluo-file-http.sh,runwsgi的启动停止管理脚本

 

#!/bin/sh

# 
# hiluo-file-http control
# $Date: 2012-07-04 willzhai $
#

# If pid file path is not set elsewhere, set to /tmp/hiluo-file-http.pid
[ -z "$PIDFILE" ] && PIDFILE="/tmp/hiluo-file-http.pid"

dir=/root/test-file-v2

start() {
    echo "Starting hiluo-file-http..."
    exec_result=`spawn-fcgi -d $dir -f $dir/upload.py -a 127.0.0.1 -p 8091`
    echo $exec_result
    if [ ! -z "$PIDFILE" ]; then
        echo $exec_result|awk -F ':' '{print $4}' > $PIDFILE
    fi
}

stop() {
    # Stop daemons.
    echo "Shutting down hiluo-file-http..."
    kill `pgrep -f "python $dir/upload.py"`
    rm -f $PIDFILE
}

restart() {
    stop
    sleep 3 # give it a few moments to shut down
    start
}

status() {
    pid=`cat $PIDFILE 2>&1`
    if [ "$?" = "1" ]; then
        echo "hiluo-file-http is not running"
        RETVAL=0
    else 
        ps -p $pid > /dev/null 2>&1
        if [ "$?" = "0" ]; then 
            echo "hiluo-file-http is running"
            RETVAL=0
        else 
            echo "hiluo-file-http is not running"
            RETVAL=0
        fi
    fi
}

case "$1" in
    start)
        start
        ;;
    stop)
        stop
        ;;
    restart)
        restart
        ;;
    status) 
        status
        ;;
    *)
        echo "Usage $0 {start|stop|restart|status}"
        RETVAL=1
esac

exit $?

注意将dir改成实际的部署路径。这个可以再改进,自动识别当前路径。

 

 

7.hiluo-file-resize.sh ,resize worker的启动停止管理脚本

 

#!/bin/sh

# 
# hiluo-file-resize control
# $Date: 2012-07-04 willzhai $
#

# If pid file path is not set elsewhere, set to /tmp/hiluo-file-resize.pid
[ -z "$PIDFILE" ] && PIDFILE="/tmp/hiluo-file-resize.pid"

dir=/root/test-file-v2

start() {
    echo "Starting hiluo-file-resize..."
    exec_command="exec python $dir/worker_resize.py 2>&1 &"
    eval $exec_command
    RETVAL=$?

    if [ $RETVAL -eq 0 -a ! -z "$PIDFILE" ]; then
        echo $! > $PIDFILE
    fi

    echo "hiluo-file-resize new pid: "`cat $PIDFILE`
}

stop() {
    # Stop daemons.
    echo "Shutting down hiluo-file-resize..."
    kill `pgrep -f "python $dir/worker_resize.py"`
    rm -f $PIDFILE
}

restart() {
    stop
    sleep 3 # give it a few moments to shut down
    start
}

status() {
    pid=`cat $PIDFILE 2>&1`
    if [ "$?" = "1" ]; then
        echo "hiluo-file-resize is not running"
        RETVAL=0
    else 
        ps -p $pid > /dev/null 2>&1
        if [ "$?" = "0" ]; then 
            echo "hiluo-file-resize is running"
            RETVAL=0
        else 
            echo "hiluo-file-resize is not running"
            RETVAL=0
        fi
    fi
}

case "$1" in
    start)
        start
        ;;
    stop)
        stop
        ;;
    restart)
        restart
        ;;
    status) 
        status
        ;;
    *)
        echo "Usage $0 {start|stop|restart|status}"
        RETVAL=1
esac

exit $?

 

注意将dir改成实际的部署路径。这个可以再改进,自动识别当前路径。

 

8.效果如下

 

8.1上传页面


 

8.2上传成功页面

 

8.3缩略图页面

 

 

文件打包下载见附件!

 

参考:

http://webpy.org/cookbook/storeupload/

http://webpy.org/cookbook/fastcgi-nginx

http://www.pythonclub.org/python-basic/string

http://hi.baidu.com/thinkinginlamp/blog/item/4b61e9241f08820f4c088d95.html

 

 

 

 

  • 大小: 6.7 KB
  • 大小: 4.7 KB
  • 大小: 20.7 KB
分享到:
评论

相关推荐

    动态缩略图实现方案

    4. **保存缩略图**:使用`ImageIO.write()`方法将缩略图保存为新的文件或写入输出流,供网络传输。 除了基本的缩放,我们还可以添加其他功能,比如裁剪、旋转、添加边框等。如果需要处理大量图像,可以考虑使用多...

    C#视频上传时自动截缩略图_例

    5. 存储与展示:生成的缩略图可以存储在服务器的特定目录,数据库中记录其路径,以便在网页上展示。在前端,使用HTML和JavaScript加载并显示这些缩略图,如在`&lt;img&gt;`标签的`src`属性中引用。 总之,实现C#视频上传...

    文件上传,生成缩略图,生成水印。

    在IT领域,文件上传、生成缩略图以及生成水印是常见的操作,广泛应用于网站开发、图像处理和数字媒体管理中。以下是对这些知识点的详细解释: 1. 文件上传: 文件上传是用户通过Web界面将本地计算机上的文件传输到...

    多图片上传-判断图片像素-生成缩略图

    对于大型网站或应用,为了提高效率,还可以采用异步处理,即先将原始图片存储,然后在后台生成缩略图。 文件名"myupload"可能指的是一个与多图片上传相关的文件,可能包含了实现上述功能的代码示例、配置文件或测试...

    好用的图片缩略图上传类

    一个优秀的图片缩略图上传类能够极大地提升用户体验,节省服务器资源,同时保持图片展示的清晰度和美观性。本文将深入探讨“好用的图片缩略图上传类”,并基于给出的标签“源码”和“工具”进行详细的分析。 首先,...

    用FileUpload控件上传图片并自动生成缩略图、带文字和图片的水印图.docx

    文件的存储路径、缩略图路径、水印图路径都是通过此方法生成的。 7. **文件上传**:如果文件存在且满足条件,`FileUpload1.SaveAs(webFilePath)`将上传的文件保存到服务器指定位置。 8. **缩略图生成**:生成缩略...

    用FileUpload控件上传图片并自动生成缩略图带文字和图片的水印图.pdf

    本篇将详细讲解如何使用ASP.NET中的FileUpload控件来实现图片上传,并结合C#后端代码自动生成带有文字和图片水印的缩略图。 首先,`FileUpload`控件是ASP.NET提供的一种用于上传文件的服务器控件。在HTML页面中,...

    php图片上传类,目录自动分割、等比例缩略图 开发技术.zip

    - 保存新生成的缩略图到服务器。 4. 安全措施: 在处理图片上传时,还需要考虑安全方面,例如: - 防止文件覆盖:确保新上传的文件不会覆盖现有文件。 - 检查文件大小:限制上传文件的最大大小,防止服务器被...

    带左右按钮和缩略图的焦点图代码.rar

    焦点/幻灯图通常指的是自动播放并循环显示的图片展示效果,与“带左右按钮和缩略图”的焦点图类似,但可能不包含手动切换的按钮。 从【压缩包子文件的文件名称列表】来看,只有一个名为“带左右按钮和缩略图的焦点...

    C#上传图片+缩略图

    本篇文章将详细解析如何使用C#语言实现图片的上传及自动生成缩略图的功能。 #### 二、核心知识点 ##### 1. 图片上传流程 - **接收用户上传的文件**:通过HTML表单提交文件,服务器端使用`HtmlInputFile`类接收文件...

    PHP上传多图片生成缩略图加水印类

    在提供的文件列表中,`uploadC.php`可能是包含图片上传和处理逻辑的类文件,它可能封装了上述提到的功能,如接收上传文件、验证、生成缩略图和加水印。`index.php`很可能是前端页面,负责展示上传界面,用户通过这个...

    asp.net 生成缩略图

    5. **异步处理**:对于大尺寸图片或者需要生成多张缩略图的情况,可以考虑使用异步操作,避免阻塞主线程,提升服务器响应速度。 6. **安全性**:在处理用户上传的图片时,需要注意安全问题,如防止图片注入攻击,...

    [图片动画]在线批量生成缩略图工具(PHP).rar

    在这个场景下,批量生成缩略图意味着程序会自动读取指定目录下的所有图片,根据预设的参数(如宽度、高度、保持比例等)生成缩略图,并保存到目标位置。 4. **缩略图生成**: 缩略图生成通常包括以下几个步骤: -...

    基于PHP的批量生成缩略图的代码.zip

    此外,使用队列处理或异步任务来分批生成缩略图,可以防止服务器过载。 6. **错误处理**:在实际应用中,可能会遇到各种问题,比如文件不存在、格式不支持、权限不足等,因此代码应该有良好的错误处理机制,记录并...

    C#(Asp.net)生成缩略图

    C#(Asp.net)作为Microsoft开发的服务器端编程语言,提供了丰富的库和功能来处理图像操作,包括生成缩略图。以下是对"生成缩略图"这一知识点的详细讲解。 1. **基本概念**: - **缩略图**:缩略图是指较小尺寸的...

    uploadify 整合thinkPHP上传加缩略图

    本教程将详细介绍如何整合`Uploadify`与`ThinkPHP`实现图片上传并自动生成缩略图。 1. **Uploadify介绍** `Uploadify`允许用户批量上传文件,支持多浏览器兼容,包括IE6。其特点在于异步上传,无需刷新页面即可...

    Ajax无刷新上传图片。(jquery + c# ashx),生成缩略图等

    这个处理程序检查是否有文件上传,然后保存文件到服务器,并调用`GenerateThumbnail`方法生成缩略图。 **生成缩略图** 生成缩略图通常涉及到图像处理库,如ImageMagick或System.Drawing。以下是一个使用System....

    PHP实例开发源码—FFmpeg视频的缩略图制作.zip

    这段代码中,`-i`参数指定输入的视频文件,`-vf 'scale=640:-2'`用来调整缩略图的尺寸,这里设置为640像素宽,高度自动保持比例。`-vframes 1`表示只抽取一帧作为缩略图,最后`$thumbnailPath`是保存缩略图的路径。 ...

    异步批量下载图片并缓存

    使用缩略图或按需加载来减少网络请求;使用GIF或WebP格式来降低图片大小。 9. **生命周期管理**:确保在Activity或Fragment的生命周期方法中正确管理和释放资源,防止内存泄漏。 10. **代码示例**:提供的"Android...

Global site tag (gtag.js) - Google Analytics