服务器端支持断点上传
将up.php拷贝到网站根目录,根目录创建uploads文件夹用于存储上传文件,配置权限.
一,普通文件上传(完成上传返回201) curl -F "action=upload" -F "Filedata=@a.file" -v "http://127.0.0.1/up.php" * About to connect() to 127.0.0.1 port 80 (#0) * Trying 127.0.0.1... connected > POST /up.php HTTP/1.1 > User-Agent: curl/7.23.1 (x86_64-unknown-linux-gnu) libcurl/7.23.1 OpenSSL/0.9.8h zlib/1.2.3 > Host: 127.0.0.1 > Accept: */* > Content-Length: 21810 > Expect: 100-continue > Content-Type: multipart/form-data; boundary=----------------------------e0b0c03a69b3 > < HTTP/1.1 100 Continue < HTTP/1.1 201 Created < Server: ngx_openresty < Date: Mon, 14 Oct 2013 02:09:55 GMT < Content-Type: text/html < Transfer-Encoding: chunked < Connection: keep-alive < * Connection #0 to host 127.0.0.1 left intact Upload OK* Closing connection #0 二,指定分片上传(未完成上传返回202并返回下一个需要上传分片信息,上传完毕返回201,上传错误返回500,对此客户端可以查询上传状态) curl -F "action=upload" -F "Filedata=@a.file" -H "Range: bytes=30720-52226/52227" -v "http://127.0.0.1/up.php" * About to connect() to 127.0.0.1 port 80 (#0) * Trying 127.0.0.1... connected > POST /up.php HTTP/1.1 > User-Agent: curl/7.23.1 (x86_64-unknown-linux-gnu) libcurl/7.23.1 OpenSSL/0.9.8h zlib/1.2.3 > Host: 127.0.0.1 > Accept: */* > Range: bytes=30720-52226/52227 > Content-Length: 21810 > Expect: 100-continue > Content-Type: multipart/form-data; boundary=----------------------------ae9de390286d > < HTTP/1.1 100 Continue < HTTP/1.1 202 Accepted < Server: ngx_openresty < Date: Mon, 14 Oct 2013 02:14:41 GMT < Content-Type: text/html < Transfer-Encoding: chunked < Connection: keep-alive < Range: bytes=0-30719/52227 < * Connection #0 to host 127.0.0.1 left intact Upload Continue* Closing connection #0 三,查询文件上传状态(上传完毕返回201,不存在此文件返回404,未上传完毕返回202并下一个需要上传的分片范围) curl -I -H "Filename: a.file" "http://127.0.0.1/up.php" HTTP/1.1 202 Accepted Server: ngx_openresty Date: Mon, 14 Oct 2013 02:16:25 GMT Content-Type: text/html Connection: keep-alive Range: bytes=0-30719/52227 四,对于大文件和超大文件可以通过多次分片上传实现 curl -F "action=upload" -F "Filedata=@big.file" -H "Range: bytes=0-102399/1024000000" -v "http://127.0.0.1/up.php" curl -F "action=upload" -F "Filedata=@big.file" -H "Range: bytes=102400-204799/1024000000" -v "http://127.0.0.1/up.php" curl -F "action=upload" -F "Filedata=@big.file" -H "Range: bytes=204800-303599/1024000000" -v "http://127.0.0.1/up.php" ... 五,对读写分片信息加文件锁还可实现多线程上传
up.php源代码如下
<?php //up.php // Define a folder to upload $targetFolder = '/uploads'; // Relative to the root function array_sort($arr) { $karr = array(); for($i=0;$i<count($arr);$i++){ $karr[$arr[$i][0]] = $arr[$i][1]; } ksort($karr); $arr = array(); foreach($karr as $k => $v){ $arr[] = array($k,$v); } return $arr; } /** * Update range of uploaded segments of file */ function updateRange($rangefile, $start, $end, $totalsize){ if(file_exists($rangefile)){ $data = json_decode(file_get_contents($rangefile)); } if(is_null($data) or is_null($data->range) or count($data->range) == 0) { $data->range = array(array($start, $end));//create a record if not exists $data->totalsize = $totalsize; } else { $range = array_sort($data->range); $c = count($range); for($i=0;$i<$c;$i++){ if($range[$i][0] > $end + 1) {// insert before $i $range[] = array($start,$end); break; } else if($range[$i][1] + 1< $start){ if($i == $c - 1){// insert end of all $range[] = array($start,$end); break; } else { continue; } } else{ $range[$i][0] = $range[$i][0] < $start?$range[$i][0]:$start; for($j = $i;$j < $c;$j ++){ if($j == $c - 1){ $range[$i][1] = $range[$j][1] > $end?$range[$j][1]:$end; if($i < $j){//combine segments unset($range[$j]); } break; } else { if($range[$j+1][0] > $end + 1){ $range[$i][1] = $range[$j][1] > $end?$range[$j][1]:$end; if($j > $i){//combine segments unset($range[$j]); } break; }else{ if($j > $i){//combine segments unset($range[$j]); } else { continue; } } } } break; } } $data->range = $range; } //save status file_put_contents($rangefile,json_encode($data)); } /** * Lookup Next Range for upload */ function lookupRange($rangefile){ if(file_exists($rangefile)){ $data = json_decode(file_get_contents($rangefile)); }else{ return array("Not Found","",""); } if(is_null($data) or is_null($data->range) or count($data->range) == 0) { return array("Not Found","",""); } else { $range = array_sort($data->range); $c = count($range); if($c == 1 && $range[0][0] == 0 && $range[0][1]+1 == $data->totalsize){ return array("Created","",""); } $start = 0; $end = $data->totalsize-1; for($i=0;$i<$c;$i++){ if($range[$i][0]<=$start){ $start = $range[$i][1]; if($i+1<$c){ $end = $range[$i+1][0]-1; } } else{ $end = $range[$i][0]-1; } break; } return array($start,$end,$data->totalsize); } } if(!is_null(@$_SERVER['REQUEST_METHOD']) && @$_SERVER['REQUEST_METHOD'] == "HEAD"){ if(is_null($_SERVER["HTTP_FILENAME"])){ header("HTTP/1.0 400 Bad Request"); exit(-1); } $filename = $_SERVER["HTTP_FILENAME"]; list($start,$end,$size) = lookupRange(rtrim($_SERVER['DOCUMENT_ROOT'] . $targetFolder,'/')."/".$filename.".range"); if("Not Found" === $start){ header("HTTP/1.0 404 File Not Found"); } else if("Created" === $start){ header("HTTP/1.0 201 Created"); } else{ header("HTTP/1.0 202 Accepted"); header("Range: bytes=$start-$end/$size"); } exit(-1); } if(!is_null(@$_SERVER['REQUEST_METHOD']) && @$_SERVER['REQUEST_METHOD'] != "POST"){ header("HTTP/1.0 400 Bad Request"); exit(-1); } if (!empty($_FILES)) { $tempFile = $_FILES['Filedata']['tmp_name']; $targetPath = $_SERVER['DOCUMENT_ROOT'] . $targetFolder; $targetFile = rtrim($targetPath,'/') . '/' . $_FILES['Filedata']['name']; if(is_null($_SERVER["HTTP_RANGE"])){ move_uploaded_file($tempFile,iconv("UTF-8","gb2312", $targetFile)); $size = $_FILES['Filedata']['size']; updateRange($targetFile.".range",0,$size-1,$size); header("HTTP/1.0 201 Created"); echo "Upload OK"; } else { $pos = $_SERVER["HTTP_RANGE"]; $range = ltrim($pos, "bytes="); list($start,$end,$size) = split('[-/]',$range); $pos = $start; $r_fp = fopen($tempFile, 'r'); if(file_exists($targetFile)){ $w_fp = fopen($targetFile, 'r+'); }else{ $w_fp = fopen($targetFile, 'w+'); } while($data = fread($r_fp, 4092)){ flock($w_fp,LOCK_EX); fseek($w_fp, $pos, SEEK_SET); fwrite($w_fp, $data); flock($w_fp,LOCK_UN); $pos += strlen($data); } fclose($r_fp); fclose($w_fp); updateRange($targetFile.".range",$start,$end,$size); list($start,$end,$size) = lookupRange($targetFile.".range"); if("Not Found" === $start){ header("HTTP/1.0 500 Internal Server Error"); echo "Upload Failed"; } else if("Created" === $start){ header("HTTP/1.0 201 Created"); echo "Upload OK"; } else { header("HTTP/1.0 202 Accepted"); header("Range: bytes=$start-$end/$size"); echo "Upload Continue"; } } } ?>