宋黎晓的空间

我们一直在努力....

构建基于Nginx的文件服务器思路与实现

标签: nginx 文件服务器 图片服务器 图片缩放
    在Web项目中使用独立的服务器来保存文件和图片的好处很多,如:便于统一管理,分流web服务器的压力,可进行访问加速等.另外当web服务器需要做集群进行负载均衡时,图片和文件上传在各个服务器之间同步将是个麻烦.关于图片服务器的方案,网上搜集到过一些,都不太合意。于是自己想了个方案,用Nginx来做图片服务器,现在已经初步实现了。下面先说说我的思路,而后在介绍一下初步是如何实现的。
    想到用Nginx来做文件服务器,是看到Nginx有几个扩展模块,分别可实现文件的上传,图片的缩放,以及访问的代理。有了这些功能,文件可上传到服务器,访问文件时前端又可做多个代理进行分流,而且Nginx自身的高并发能力又没的说,另外还附带了一个图片缩放的功能,干嘛不用呢?
于是着手研究了一下这几个模块。发现只有一点不符合我们的要求,那就是文件上传模块的机制是支持在Nginx配置一个文件上传的url,此URL接收提交的文件并将文件临时放到Nginx所在主机的一个指定目录,而后转发请求给后台程序(也就是我们自己的web程序),由我们自己的程序实现移动文件和将文件路径等信息写入数据库的工作。这也就要求我们的后台处理程序要跟Nginx部署在同一台主机,要不然怎么能够移动文件呢?这显然不符合我们的初衷----将文件服务器独立于其他Web应用。如果我们能解决移动文件的问题,就可以清除障碍了。
    要解决移动文件,需要如下几步:
1.  利用文件上传模块原有机制,将上传的文件保存在临时目录。
2.  移动文件到我们期望的目录,并更改文件名防止重名。
3.  将移动后的目录以及文件名称等信息转发给后台web程序,由web程序自己将信息写入自己的数据库。
第一步Nginx上传模块已经实现,我们只要可以移动文件并转发请求就可以了。转发请求是nginx的强项,这个不用担心。移动文件Nginx自身却没有这个能力。有两种方法实现:1. 做一个web程序与nginx部署在一起,负责移动文件。2. 想办法让Nginx来完成移动文件。显然第一种方式比较容易实现,但第二种方式才是我想要的。
Nginx有一个扩展模块lua_nginx,此模块支持在Nginx上使用基于c的lua脚本。有脚本语言支持,可编程性就大大提高了,要完成我们移动文件的目的当然不在话下。
下面介绍下我设想的这几个模块综合应用后的文件及图片服务器的结构:

如上图,至少需要3太nginx服务器,分别负责图上标示的这些功能。当然,如果不需要将图片的访问做负载均衡,所有功能集中在一台服务器上也是可以的。
下面是实现上述功能的Nginx的安装以及配置(【路由与负载均衡】部分就不详细介绍了,这方面的资料很多。):
Nginx的安装网上很多介绍,这里不再详细说了。为了附加扩展模块,我们不能使用yum的安装方式。只能下载代码包以及扩展模块的代码包,然后使用./configure 然后make install的方式来安装。安装过程中可能会遇到一些问题,基本是缺少一些依赖什么的,根据错误提示,下载和安装缺少的软件包后就可以解决。另外,我曾遇到过upload模块与Nginx版本冲突的问题,以及make时报“md5.h 没有那个文件或目录”的错误,在我另外一篇文章里有介绍(CentOS下安装Nginx并添加nginx_upload_module).
下面先介绍下文件上传服务器的安装以及我的配置。依赖问题解决后,使用下面的脚本安装:
java 代码
  1. ./configure --prefix=/B2B/servers/nginx --add-module=/B2B/tars/masterzen-nginx-upload-progress-module-a788dea --add-module=/B2B/tars/nginx_upload_module-2.2.0 --add-module=/B2B/tars/lua-nginx-module-master --with-pcre=/B2B/tars/pcre-8.21 --with-openssl=/B2B/tars/openssl-1.0.0e 
里面分别附加了nginx_upload_module(用来接收上传文件并临时保存),nginx-upload-progress-module(可获得上传进度)lua-nginx-module(使nginx支持lua脚本,用来移动文件转发请求给后台)。
下面先上配置文件nginx.conf
js 代码
  1. #user  nobody;  
  2. worker_processes  1;  
  3.  
  4. #error_log  logs/error.log;  
  5. #error_log  logs/error.log  notice;  
  6. #error_log  logs/error.log  info;  
  7.  
  8. #pid        logs/nginx.pid;  
  9.   
  10.   
  11. events {  
  12.     worker_connections  1024;  
  13. }  
  14.   
  15.   
  16. http {  
  17.     include       mime.types;  
  18.     default_type  'text/html';  
  19.     sendfile        on;  
  20.     keepalive_timeout  65;  
  21.        
  22.     server {  
  23.         listen       80;  
  24.         server_name  localhost;  
  25.           lua_code_cache off;  
  26.           set $callback_url "/";  
  27.         location / {  
  28.             root   html;  
  29.             index  index.html index.htm;  
  30.                       }  
  31.   
  32.         location /upload{  
  33.                 client_max_body_size 35m;                # 上传文件大小限制  
  34.                 upload_cleanup 500-505;    # 发生这些错误删除文件 400 404 499 500-505  
  35.                 upload_store_access user:rw group:rw all:rw;            # 访问权限  
  36.                 # upload_limit_rate 128k;                 # 上传速度限制  
  37.                 upload_pass_args on;                        # 允许上传参数传递到后台  
  38.   
  39.                 if ($uri ~* "^/upload/(.*)") {  
  40.                     set $sub_path $1;  
  41.                 }  
  42.                 if ($uri !~* "^/upload/(.*)") {  
  43.                     set $sub_path "default";  
  44.                 }  
  45.   
  46.                 if (-d $cookie_username) {  
  47.                     set $user_name $cookie_username;  
  48.                 }  
  49.                 if (!-d $cookie_username){  
  50.                     set $user_name "nobody";  
  51.                 }  
  52.   
  53.   
  54.   
  55.                 upload_store /B2B/uploadfiles/temp;                # /B2B/uploadfiles/用户/日期/文件类型/文件名        # 本地存储位置  
  56.   
  57.                 upload_set_form_field "callback" $arg_callback;  
  58.                 upload_set_form_field "use_date" $arg_use_date;  
  59.                 upload_set_form_field "sub_path" $sub_path;   
  60.                 upload_set_form_field "user_name" $user_name;   
  61.                 upload_set_form_field "file_name" $upload_file_name;   
  62.                 upload_set_form_field "file_content_type" $upload_content_type;  
  63.                 upload_aggregate_form_field "file_md5" $upload_file_md5;  
  64.             upload_aggregate_form_field "file_size" $upload_file_size;  
  65.                 upload_set_form_field "temp_path" $upload_tmp_path;  
  66.                 upload_pass_form_field ".*";  
  67.   
  68.                 upload_pass /prossfile; # 转给文件处理(移动文件,转发请求)  
  69.                      }  
  70.             # 处理文件:使用lua脚本处理文件,将文件移动并重命名到特定的文件夹。而后将文件信息转发给后台处理程序。  
  71.         location /prossfile{  
  72.                 lua_need_request_body on;  
  73.                 content_by_lua_file /B2B/servers/nginx/luas/onupload.lua;  
  74.                      }  
  75.  
  76.           # 文件上传后台程序处理路径  
  77.          include /B2B/servers/nginx/conf/upload_callback.conf;  
  78.  
  79.            # 文件访问路径  
  80.          location /files/{  
  81.                 default_type  'application/octet-stream';  
  82.                 alias /B2B/uploadfiles/;  
  83.            }  
  84.         }  
其中location /upload接收文件的上传放到的临时目录,并整理参数,而后转发给location /prossfile。location /prossfile将使用lua脚本来处理文件的移动并再次转发请求给后台的网站。
另外,include /B2B/servers/nginx/conf/upload_callback.conf;这一句引入了另一个配置文件,这里面配置的一些location是后台的web应用用来接收文件上传的url地址。如:
js 代码
  1. location /B2B {  
  2.               proxy_pass http://192.168.3.32:8080/cookie.test/index.jsp;  
  3.          }  
  4.        location /example {  
  5.               proxy_pass http://www.oecp.cn;  
  6.          } 
实现文件移动和转发请求的部分在lua里面,也是重点部分。在实现时遇到一些麻烦,可能是lua这个模块与上传模块的冲突(具体原因不详),文件上传模块将请求转发到lua后,文件信息和form里的内容居然成了无法识别的。正常情况下,应该可以转化为一种table对象,以key-value来存取,但现在确是一个只有一行的table,key和value都是很长的字符串,为此,只能暂时用分离字符串的方式将form的内容拆分出来,而后才能转发给后台处理的web应用。
下面是脚本luas/onupload.lua的具体内容:
cpp 代码
  1. function onupload()  
  2.     ngx.req.read_body();  
  3.     local post_args = ngx.req.get_post_args();                    -- 读取参数  
  4.     local tab_params = getFormParams_FixBug(post_args);        -- 处理参数错误  
  5.   
  6.     pressFile(tab_params);        -- 处理文件  
  7.     -- ngx.log(ngx.ERR,"#############@" ,tab_params["callback"],"@###########");  
  8.     if (tab_params["callback"] and tab_params["callback"] ~= "") then  
  9.         ngx.exec(tab_params["callback"],tab_params);  -- 转发请求  
  10.     else  
  11.         ngx.say("Callback not specified!!");  
  12.     end  
  13. end  
  14.   
  15.   
  16. --[[  
  17.     处理文件  
  18.     主要进行 创建目录 & 移动文件 等操作。  
  19. ]]  
  20. function pressFile(params)  
  21.     local dirroot = "/B2B/uploadfiles/";  
  22.     local todir = params["user_name"].."/";  
  23.     if(params["sub_path"]) then  
  24.         todir = params["sub_path"].."/"..todir;  
  25.     end  
  26.     if(trim(params["use_date"]) == "Y") then  
  27.          todir = todir..os.date('%Y-%m-%d').."/"  
  28.     end  
  29.     todir = trim(todir);  
  30.     local tofile = todir..params["file_md5"]..getFileSuffix(params["file_name"]);  
  31.     tofile = trim(tofile);  
  32.   
  33.     local sh_mkdir = "mkdir -p " ..dirroot..todir;  
  34.     local sh_mv = "mv "..trim(params["temp_path"]).." "..dirroot..tofile;  
  35.   
  36.     params["file_path"] = tofile;  
  37.     if(os.execute(sh_mkdir) ~= 0) then  
  38.         ngx.exec("/50x.html");  
  39.     end  
  40.     if(os.execute(sh_mv) ~= 0) then  
  41.         ngx.exec("/50x.html");  
  42.     end  
  43. end  
  44.   
  45. function getFileSuffix(fname)  
  46.     local idx,idx_end = string.find(fname,"%.");  
  47.     return string.sub(fname,idx_end);  
  48. end  
  49.   
  50. function trim(str)  
  51.     if(str ~= nil) then  
  52.         return string.gsub(str, "%s+""");  
  53.     else  
  54.         return nil;  
  55.     end  
  56. end  
  57.   
  58. function urlencode(str)  
  59.     if (str) then  
  60.         str = string.gsub (str, "\n""\r\n")  
  61.         str = string.gsub (str, "([^%w ])",  
  62.         function (c) return string.format ("%%%02X", string.byte(c)) end)  
  63.         str = string.gsub (str, " ""+")  
  64.     end  
  65.     return str  
  66. end  
  67. function urldecode(str)  
  68.   str = string.gsub (str, "+"" ")  
  69.   str = string.gsub (str, "%%(%x%x)",  
  70.       function(h) return string.char(tonumber(h,16)) end)  
  71.   str = string.gsub (str, "\r\n""\n")  
  72.   return str  
  73. end  
  74.   
  75. --[[  
  76.  * 修复form提交后参数转发丢失问题。  
  77.  * 文件上传成功后,转发到另一个URL作后继处理。此时表单数据和文件信息丢失。原因不明,猜测可能是上传模块与lua模块冲突导致。  
  78.  * 转发过来的from内容lua收到后现为如下形式的table对象:  
  79. -----------------------------5837197829760   
  80. Content-Disposition: form-data; name"test_name"   
  81.    
  82. 交易.jpg   
  83. -----------------------------5837197829760   
  84. Content-Disposition: form-data; name="test_content_type"   
  85.    
  86. image/jpeg   
  87. -----------------------------5837197829760  
  88.  * 因此自行处理来分离出表单内容。  
  89.  * 使用分离字符串的方式。注意!!!字段名称中不能使用半角双引号。  
  90. ]]  
  91. function getFormParams_FixBug(post_args)  
  92.     local str_params;  
  93.     if (post_args) then  
  94.         for key,val in pairs(post_args) do  
  95.             str_params = key ..val;  
  96.         end  
  97.     else  
  98.         return nil;  
  99.     end  
  100.   
  101.     local tab_params = {};  
  102.     local str_start = " name";  
  103.     local str_start_len = string.len(str_start);  
  104.     local str_end = "%-%-%-%-%-%-%-%-%-%-%-%-%-%-%-%-%-%-%-%-%-%-%-%-%-%-%-%-%-";  
  105.     local str_sign = "\"";  
  106.     local idx,idx_end = string.find(str_params,str_start);  
  107.     local i = 0;  
  108.   
  109.     -- 如果字符串内仍有开始标记,则说明还有内容需要分离。继续分离到没内容为止。  
  110.     while idx do  
  111.         str_params = string.sub(str_params,idx_end); -- 截取开始标记后所有字符待用  
  112.         i = string.find(str_params,str_sign); -- 查找字段名开始处的引号索引  
  113.         str_params = string.sub(str_params,i+1); -- 去掉开始处的引号  
  114.         i = string.find(str_params,str_sign); -- 查找字段名结束位置的索引  
  115.         f_name = string.sub(str_params,0,i-1); -- 截取到字段名称  
  116.   
  117.         str_params = string.sub(str_params,i+1); -- 去掉名称字段以及结束时的引号  
  118.         i,i2 = string.find(str_params,str_end); -- 查找字段值结尾标识的索引  
  119.         f_value = string.sub(str_params,1,i-1); -- 截取到字段值  
  120.         tab_params[f_name] = f_value;  
  121.         idx = string.find(str_params,str_start,0); -- 查找判断下一个字段是否存在的  
  122.     end  
  123.     tab_params["callback"] = urldecode(trim(tab_params["callback"]));  
  124.       
  125.     return tab_params;  
  126. end  
  127.   
  128. onupload(); 
脚本与配置文件相互配合,根据用户上传时的提交的路径和当前用户,是否使用日期区分等参数,来创建子文件夹,将文件移动至存放目录。并使用文件的md5值来作为文件名,以防止重名。最后根据url参数里的callback参数来转发文件信息到后台的web程序。
至此文件上传功能就完成了,然后我们再来看一下文件缩放以及代理的安装和配置:
文件缩放模块的安装比较简单,不需要太多的依赖,安装过程也不会遇到太多问题。也不需要多说,安装前,在./configure时候添加 –with-http_image_filter_module就行了。
下面上一下配置
js 代码
  1. #user  nobody;  
  2. worker_processes  1;  
  3.  
  4. #error_log  logs/error.log;  
  5. #error_log  logs/error.log  notice;  
  6. #error_log  logs/error.log  info;  
  7.  
  8. #pid        logs/nginx.pid;  
  9.   
  10.   
  11. events {  
  12.     worker_connections  1024;  
  13. }  
  14.   
  15.   
  16. http {  
  17.     include       mime.types;  
  18.     default_type  application/octet-stream;  
  19.  
  20.     #log_format  main  '$remote_addr - $remote_user [$time_local] "$request" '  
  21.     #                  '$status $body_bytes_sent "$http_referer" '  
  22.     #                  '"$http_user_agent" "$http_x_forwarded_for"';  
  23.  
  24.     #access_log  logs/access.log  main;  
  25.   
  26.     sendfile        on;  
  27.     #tcp_nopush     on;  
  28.  
  29.     #keepalive_timeout  0;  
  30.     keepalive_timeout  65;  
  31.  
  32.     #gzip  on;  
  33.   
  34.     server {  
  35.         listen       80;  
  36.         server_name  localhost;  
  37.  
  38.         #charset koi8-r;  
  39.  
  40.         #access_log  logs/host.access.log  main;  
  41.   
  42.         location / {  
  43.             root   html;  
  44.             index  index.html index.htm;  
  45.                       }  
  46.   
  47.       location /img {  
  48.         #  图片被代理过来以后实际存放的根目录  
  49.         alias /tmp/nginx/resize;  
  50.         set $width 9999;  
  51.         set $height 9999;  
  52.         set $dimens "";  
  53.  
  54.          # 请求中带有尺寸的,分离出尺寸,并拼出文件夹名称  
  55.         if ($uri ~* "^/img_(\d+)x(\d+)/(.*)" ) {  
  56.             set $width $1;  
  57.             set $height $2;  
  58.             set $image_path $3;  
  59.             set $demins "_$1x$2";  
  60.         }  
  61.         if ($uri ~* "^/img/(.*)" ) {  
  62.             set $image_path $1;  
  63.         }  
  64.  
  65.         # 本地没有找到图片时,调用获取图片并压缩图片的连接  
  66.         set $image_uri img_filter/$image_path?width=$width&height=$height;  
  67.           if (!-f $request_filename) {  
  68.             proxy_pass http://127.0.0.1:80/$image_uri;  
  69.             break;  
  70.         }  
  71.         proxy_store /tmp/nginx/resize$demins/$image_path;  
  72.         proxy_store_access user:rw group:rw all:rw;  
  73.         proxy_temp_path /tmp/images;  
  74.         proxy_set_header Host $host;  
  75.   
  76.     }  
  77.     # 此处为图片实际地址,可以为远程地址  
  78.    location /img_filter/ {  
  79.             image_filter_buffer 20M;  
  80.          proxy_pass http://192.168.3.239/files/;  
  81.             image_filter resize $arg_width $arg_height;  
  82.             image_filter_jpeg_quality 75;  
  83.             allow 127.0.0.0/8;  
  84.             deny all;  
  85.         }  
  86. }  
上面配置中,各个部分比较容易读懂,而且写了注释,不再过多解释。
服务配置好以后我们能够用它做什么,怎么用呢?
当我们需要上传文件的时候,页面将文件post到上传服务器如:http://192.168.3.239/upload/test?use_date=Y&callback=/example
服务器将会这样存放文件 test/username/日期/md5.扩展名 ,其中test是来自于url中/upload/后面的部分一直到问号之前,url路径中除了/upload/是固定的,后面都是可以自己定义的,用来指定文件夹结构;username是来自于cookie里的username属性,如果没有的话,默认为nobody;日期是可选的,根据url后的参数use_date是否等于Y来决定是否区分日期子文件夹;callback参数是用来指定上传完成后,用来后继处理的web程序的url,但是这个url不能直接写连接,只能使用配置在conf/upload_callback.conf里面的location,这么做有连个原因,直接写路径提交给Nginx后,将会被URLEncode,nginx跳转时不能使用,另外也可以防止非法的使用者自定义跳转到其他地址。上传成功后转发给后台的参数如下:
参数名 含义
callback 上传完成后跳转到的路径
use_date 是否需要日期做子文件夹
sub_path 指定的子文件夹
user_name 上传的用户名
file_name 原始文件名
file_path 文件在服务器存放的相对路径(包括文件名)
file_content_type 文件类型
file_md5 文件的md5
file_size 文件的大小
temp_path 文件上传到Nginx后的临时目录名

图片上传完成后,访问”http://192.168.3.240/img/ test/username/日期/md5”.扩展名就可以打开图片了,如果需要缩放图片可以访问http://192.168.3.240/img_长x宽/test//username/日期/md5来获得缩放后的图片。图片的访问和缩放都经过图片缩放主机的代理做缓存处理,只有第一次访问的时候才会请求图片上传服务器,以此来获得镜像加速,使集群成为可能。
至此,我们需要的功能都有了。上面的配置和代码只实现了基础的功能,可能还会有没有想到的地方以后可以继续完善。如有疑问或建议,请与我联系。

提供该文档的机构为 OECP社区,更多的博客文章可以到 OECP社区查看。该文档附件欢迎各位转载,但是在没有获得文章作者许可之前,不得对文章内容或者版权信息进行更改,版权归OECP社区所有,仅此声明。

附件:


    评分: 请先登录再投票,同一篇博客一月只能投票一次!
    无人投票

相关博客:


评论

谭明智 2013-5-30 8:41:45   回复
为啥不使用成熟的文件系统呢,还可以更方便的管理图片,图片多了,还可以分布式部署,对前端调用没有任何的影响。很多文件系统还可以追加其他信息,保存图片的时候可以将一些元数据保存起来,说不定以后可能就用到了呢。
我们利用文件系统做的文件存储,又增加了一个管理端,能对图片进行修改和删除,同时能对图片进行二次组织,从而在未来可能会衍生出其他的产品,比如图册、搜图等等。
我的想法是,分布式文件系统做集中存储,nginx做缓存加速,两层架构。
宋黎晓 2013-5-30 13:45:20   回复
@谭明智 :你说的成熟文件系统是指的什么?GridFS?
重点不一样,我想要的不是文件存储和管理,而是文件上传的效率和访问的加速负载均衡等。而且图片的缩放,对目前的项目也很需要。 有了这些,相关的代码就不需要大家来写了。
再就我不想把图片放到数据库里面,看起来太不直观,虽然可以有客户端。要做客户端,我的方式也可以做客户端。
分布式部署其实也不成问题,根据用途或者根据时间,只是个路由配置。Nginx来做也很简单。
谭明智 2013-5-31 9:19:52   回复
@宋黎晓 :用的比较多的是moogefs,现在有很多人开始使用gfs和hadoop的hdfs来做了。上传的效率我认为可以不需要太特别关注,因为本身上传的频率不会太高,而且都是图片这种小文件。主要是访问的速度,访问速度不管用磁盘存储还是用文件系统存储,都不能有本质的改善。影响图片加载效率的因素主要是带宽和线路,如果规模大,最好的加速方式是采用CDN,利用内容分发网络做多结点缓存。
对于我们来说我认为最重要的是还是做到可管理的,可重复利用的存储。
我们提取的文件服务,解决了三个问题,第一是存储,可管理的存储,可扩展的存储,很容易分布式部署,也非常容易做容灾备份,这些都是利用文件系统本身的能力,只需要简单配置即可。第二是统一访问,所有的图片访问都交给该服务来做,所有的优化也是在这个服务上优化,可以采用两级缓存,在最前端缓存到内存,用nginx+memcached,缓存热点图片,在tomcat提供的读取服务,可以缓存到磁盘,第三层就是集中式的文件存储,前面两层的数据全部删掉,也保证了集中存储完整的数据存在。第三,提供图片的简单的处理,就是你说的,切图、缩图、加水印等功能,各个应用也可以不用做这些工作。以上三点在我们现有的文件系统上都已经基本支持了。
我更倾向采用文件系统来做,主要考虑到文件的集中存储后规模扩大,可以很容易的利用文件系统的特性做分布式扩展,同时利用文件系统的安全机制,做容灾备份,一个节点如果出现问题,服务可以很容易的平滑切换。其次,我看了很多的网站他们的图片都进行了二次利用,扩展到搜图、弄成图集,这些会衍生出其他的产品,所以我们增加了对图片的管理,文件系统不仅可以存储图片,而且可以追加很多属性,这样就可以保证存储到文件系统的图片不仅仅是一张图,而是一个有含义的内容,方便二次利用。
如果只是做存储和读取,我也倾向nginx,甚至直接存储本地,分布式后可采用ftp分发。我可能考虑的有点多,从项目考虑很多东西现在还用不上,但是代价也不大,就这么做了。而且nginx有个直接读取GridFS的模块,可以直接由nginx直接读取后端存储的,文件系统都有索引,并且不会像直接存储磁盘会形成很多碎片,IO压力不大,nginx辅助缓存,效率应该也不是问题。
只是分享了一下我的思路,这么长,可以写一篇文章出来了,呵呵。
谭明智 2013-5-31 9:29:15   回复
@宋黎晓 :http://www.oecp.cn/hi/yongtree/blog/2575  以前做电子商务时的思路,又翻出来了,我现在做的文件服务算是这个思路的实现吧。
宋黎晓 2013-5-31 13:06:41   回复
@谭明智 :原来成熟的文件系统是说的你们自己做的啊?
我这个就是自己搭一个主要功能都有,简单易用的。满足项目需要就是了,没时间和精力去搞那么一套东西。说那么多不如有现成的共享出来,我们也用用。
谭明智 2013-6-3 8:54:00   回复
@宋黎晓 :文件系统不是我们自己做的,我们做的只是针对gridfs做的服务,方便项目调用直接存储,同时做了一个统一的读取文件的端和管理端。
访问www.doctorpda.comwww.med330.cn等所有的医师在线网站的图片和文件,基本上使用的是我们同一套服务,图片的切图,缩图,水印,基本上隐含在文件路径上。本来一直想整理一下做个分享,一直没也没抽出时间,而且这个服务还不完善,很多想法还没实现。访问http://static.med626.com.cn/admin可以访问文件服务后台,简单贴几个图看看吧,以后再完整的分享一下。

?





?

游客 2013-12-8 19:44:46  
你好楼主,我原来是用java+tomcat,但是压力一上来nginx老报后端连接超时,所以想改为楼主这种方案,希望能抗压谢谢f分享
游客 2014-7-18 10:23:07  
怎么没有看到数据库处理的部分?楼主能不能说明一下啊?谢谢
宋黎晓 2014-7-18 12:42:11   回复
数据库处理部分需要自己的程序来完成,Nginx不做这个操作。文件上传后会把文件信息,包括文件名,路径,md5值等转发到指定的callback URL,这个URL就是我们自己写的程序,接收到转发和参数后,需要自己把文件信息保存到数据库中。
吴辉 2014-8-27 16:17:11   回复
怎么下载?

发表评论

验证码: