用下面用nginx+mongodb+php实现一个简单的存储系统,提供简单的文件上传下载功能(下载支持Range头)。
(后台用磁盘和mongo的gridfs分别存储大于1M和小于等于1M的文件)
A,前台文件上传页面(同时列举所有文件)(test.php)
<html> <head> <meta http-equiv="content-type" content="text/html;charset=utf-8" /> </head> <body> <!--查询meta中所有文件--> <ul> <?php $mongo = new Mongo(); $db = $mongo->selectDB("ciaos"); $meta = $db->meta; $cursor = $meta->find(); foreach ($cursor as $doc) { $filename = $doc["filename"]; echo "<li><a href=\"dl/$filename\">$filename</a></li>"; } ?> </ul> <form enctype="multipart/form-data" action="upload.php" method="POST"> <!-- Name of input element determines name in $_FILES array --> Send this file: <input name="Filedata" id="Filedata" type="file" /> <input name="submit" type="submit" value="Send File" /> </form> </body> </html>
B,服务器端文件上传(upload.php)
<?php // 存放文件的磁盘位置 $targetFolder = '/files'; if (!empty($_FILES)) { //获取上传文件的临时文件,大小,设置存储路径 $tempFile = $_FILES['Filedata']['tmp_name']; $size = $_FILES['Filedata']['size']; $targetPath = $_SERVER['DOCUMENT_ROOT'] . $targetFolder; $targetFile = rtrim($targetPath,'/') . '/' . $_FILES['Filedata']['name']; $mongo = new Mongo(); $db = $mongo->selectDB("ciaos"); //大于1M的文件放在磁盘中,然后写meta信息 if($size > 1024*1024) { move_uploaded_file($tempFile,$targetFile); $newfile = array( "filename" => $_FILES['Filedata']['name'], "savename" => $targetFile, "size" => $size ); } else { //小文件存放在mongofs中,然后写meta $gridfs = $db->getGridFS(); $mid = $gridfs->put($tempFile); $newfile = array( "filename" => $_FILES['Filedata']['name'], "mid" => $mid, "size" => $size ); } //meta中存储文件名信息,mongoID或者存储名称以及文件大小 $meta = $db->meta; $meta->save($newfile); echo "Upload succeed"; } else { echo "Upload failed"; } exit(); ?>
C,文件下载逻辑(download.php)
<?php $file = isset($_GET["file"])?$_GET["file"]:NULL; if ($file) { //去meta中查询文件信息 $mongo = new Mongo(); $db = $mongo->selectDB("ciaos"); $meta = $db->meta; $search = array( "filename"=>$file ); $search_result = $meta->findOne($search); $size= $search_result["size"]; //如果文件大于1M,转到磁盘下载 if($size > 1024*1024) { header("Content-type: application/octet-stream"); header("X-Accel-Redirect: /files/" . $file); } else { //小文件则从mongofs中读取存放在临时文件中,然后转临时文件下载 $gridfs = $db->getGridFS(); $mid = $search_result["mid"]; $fsfile = $gridfs->get($mid); $tmpname = "tmp".time(); file_put_contents("./files/$tmpname",$fsfile->getBytes()); header("Content-type: application/octet-stream"); header("X-Accel-Redirect: /files/" . $tmpname); } } else { echo "Invalid filename"; } ?>
如果下载文件名为download.php,可以加上这样一个头指定文件名header("Content-Disposition: attachment; filename= $filename");
daemon启动方法:bin/mongod --fork --logpath /data/db/mongodb.log(需要有/data/db/目录用于存放数据)
D,数据库中存储小文件内容以及meta信息,查询结果分别如下
> db.fs.files.find() { "_id" : ObjectId("50b5d1a892a5f27a0f000001"), "filename" : "/usr/local/NSP/var/php_upload_tmp/phpmgM6IS", "uploadDate" : ISODate("2012-11-28T08:56:08.480Z"), "length" : 13, "chunkSize" : 262144, "md5" : "d4369b7a8614277c011ef987ca4e16ee" } { "_id" : ObjectId("50b5ddb592a5f2452f000001"), "filename" : "/usr/local/NSP/var/php_upload_tmp/php4hu6fi", "uploadDate" : ISODate("2012-11-28T09:47:33.094Z"), "length" : 13, "chunkSize" : 262144, "md5" : "d4369b7a8614277c011ef987ca4e16ee" } >
> db.meta.find() { "_id" : ObjectId("50b5d8ca92a5f2b42d000000"), "filename" : "dbank-sdk-cpp-0.5.5.zip", "savename" : "/usr/local/NSP/htdocs/files/dbank-sdk-cpp-0.5.5.zip", "size" : 4380805 } { "_id" : ObjectId("50b5ddb592a5f2452f000003"), "filename" : "测试.txt", "mid" : ObjectId("50b5ddb592a5f2452f000001"), "size" : 13 } >
E,nginx核心配置(nginx.conf)
location / { index index.html index.htm index.php; rewrite ^/dl/(.*) /download.php?file=$1 last; } location /files { root /usr/local/ciaos/html/; internal; } location ~ .*\.php$ { fastcgi_index index.php; fastcgi_pass unix:/usr/local/ciaos/php_fcgi.socket; include /usr/local/ciaos/fastcgi.conf; }
nginx的配置主要需要注意下面几点:
1,支持php服务器端脚本运行
2,配置下载目录/files,设置为internal内部访问,这样可以避免用户直接下载磁盘,可以自定义鉴权等功能
3,配置路径转发,前台的下载路径看起来就是这样http://localhost/dl/test.txt,后台通过download.php实现下载功能。