服务器端支持断点上传

将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";
        }
    }
}
?>
原文地址:https://www.cnblogs.com/ciaos/p/3367027.html