web站点当中很多都有用户上传文件的需求,处理这种需求最简单的方法采用客户端通过post表单,服务器端通过动态页面去处理该post表单把文件保存起来,保存在服务器端。这也是目前大部分站点采用的一种架构。该架构存在以下几个问题点:
1.有可能同一个文件被N个用户都上传到服务器上保存着。这样既浪费用户流量,也浪费宝贵的服务器流量,而且还消耗存储空间。
2.文件大小的规避,js无法做到在上传前检查文件大小,传统保单通过post把文件上传到服务器端,由服务器端去处理,这样要是上传了一个大文件,超过服务器端设置的最大大小,返回给用户文件过大的提示,这样既耗费用户的带宽,服务器带宽,服务器端处理CPU时间,用户的感受也不好。
3.上传大文件,多文件,用户只能看到一个提交而无法看到上传的进度。
4.服务器端采用动态页面处理用户post的文件,如果还计算文件md5,文件大小计算等操作。动态页面处理效率很低下。
针对上面问题,加速用户上传,使用户的感受变好,同时减少服务器端资源的消耗。一般的用户99%都会安装flash插件,基于falsh插件,有一个swfupload的开源软件,
官网地址为:http://www.swfupload.org/
demo页面为:http://demo.swfupload.org/v220/index.html
该插件能在客户端验证文件大小,文件类型,上传大文件,多文件时能看到上传进度。而对于怎么验证文件是否在服务器端,我目前想到的方法是通过flash计算文件的md5值,文件的大小。先把这些信息post到服务器端,服务器端通过动态页面接收该请求,在服务器端通过该信息,在自己的信息数据库中先比对md5值和文件的大小是否一致,也许有人会问,比对了md5还比对文件大小干嘛,毕竟还存在md5碰撞的可能,比对文件大小能多加一个条件去解决,同md5不同文件的可能。如果还要求严格一点可以读取文件的前多少个字节一块上传到服务器端,进行比对,如果服务端存在该文件,返回一个上传成功,如果服务器端不存在该文件,再采用一般做法去上传文件。服务器端的话,现有很多web服务开始使用nginx做为WEB服务器了,nginx有个upload模块,安装该模块后,由于nginx本身处理并发能力就很强,加了一个这样的模块,该模块先接受用户上传的文件,并把上次成功的文件,计算出文件的md5,文件大小的东西,同时能把一些附加post表单传给后端的动态页面,动态页面,只要把该文件挪动一个位置到指定保存的地方就可以了。
最后附一个改模块安装后的一个nginx配置文件。
user www www; worker_processes 8; error_log /data/logs/nginx_error.log error; pid /var/run/nginx.pid; worker_rlimit_nofile 51200; events { use epoll; worker_connections 51200; } http { include mime.types; default_type application/octet-stream; charset utf-8; server_names_hash_bucket_size 128; client_header_buffer_size 32k; large_client_header_buffers 4 32k; # upload file size client_max_body_size 10m; sendfile on; tcp_nopush on; keepalive_timeout 300; tcp_nodelay on; log_format main '$remote_addr - $remote_user [$time_local] ' '"$request" $status $body_bytes_sent "$http_referer" ' '"$http_user_agent" "$http_x_forwarded_for"'; fastcgi_connect_timeout 300; fastcgi_send_timeout 300; fastcgi_read_timeout 300; fastcgi_buffer_size 128k; fastcgi_buffers 4 128k; fastcgi_busy_buffers_size 128k; fastcgi_temp_file_write_size 128k; fastcgi_temp_path /dev/shm/fastcgi_temp; client_body_temp_path /dev/shm/client_body_temp; gzip on; gzip_min_length 1k; gzip_buffers 4 16k; gzip_http_version 1.0; gzip_comp_level 3; gzip_types text/plain application/x-javascript text/css application/xml; gzip_vary on; server { listen 80; server_name upload.example.com; index index.php; root /data/gameprotect; error_page 403 404 405 /error/error.html; access_log /data/logs/access.log main; location ~ .*.php?$ { include /usr/local/nginx/conf/enable_php5.conf; } location /upload { limit_rate 10k; # Pass altered request body to this location upload_pass @post; # Store files to this directory # The directory is hashed, subdirectories 0 1 2 3 4 5 6 7 8 9 should exist upload_store /tmp 1; # Allow uploaded files to be read only by user upload_store_access user:r; # Set specified fields in request body upload_set_form_field "${upload_field_name}_name" $upload_file_name; #upload_set_form_field "${upload_field_name}_content_type" $upload_content_type; upload_set_form_field "${upload_field_name}_path" $upload_tmp_path; # Inform backend about hash and size of a file upload_aggregate_form_field "${upload_field_name}_md5" $upload_file_md5; upload_aggregate_form_field "${upload_field_name}_size" $upload_file_size; #upload_pass_form_field "^submit$|^description$"; upload_pass_form_field "^.*$"; } # Pass altered request body to a backend location @post { #proxy_pass http://localhost:8080; rewrite ^(.*)$ /index.php last; } } }
转载请注明:爱开源 » WEB站点文件上传杂谈