PHP HTTP 1.1无法执行部分PUT请求

时间:2013-08-13 15:22:40

标签: php sockets http put

我的网站上有一个文件上传表单,当用户将其文件上传到我的服务器时,我想将这个上传的文件从我的服务器传输到云服务器。文件可能很大,所以我决定将文件以100KB的份数发送到云端。

这是我的PHP代码示例,它尝试将文件从我的服务器发送到云端:

<?php
$cloud_server = '%my_server%';
$uploading_file_name = '%folders%/file.mp3';
$auth_token = '%token%';

$file_path = $_SERVER["DOCUMENT_ROOT"] . '/file.mp3';
$file_handler = fopen($file_path, 'rb');
$file_size = filesize($file_path);
$chunk_size = 100 * 1024;

$start_byte_position = 0;

while (!feof($file_handler)) {
    $chunk_content = fread($file_handler, $chunk_size);
    $chunk_content_length = strlen($chunk_content);
    $final_byte_position = $start_byte_position + $chunk_content_length;

    $socket_handler = fsockopen($cloud_server, 80, $errno, $errstr);
    if (!$socket_handler)
        die ($errstr);

    //Headers
    $request = "PUT /{$uploading_file_name} HTTP/1.1\r\n";
    $request .= "Connection: Close\r\n";
    $request .= "Host: {$cloud_server}\r\n";
    $request .= "X-Auth-Token: {$auth_token}\r\n";
    $request .= "Content-Range: {$start_byte_position}-{$final_byte_position}/{$file_size}\r\n";
    $request .= "Content-Length: {$chunk_content_length}\r\n";
    $request .= "\r\n";

    //Body
    $request .=  $chunk_content;
    $request .= "\r\n";

    fwrite($socket_handler, $request);

    $server_response = '';
    while (!feof($socket_handler))
        $server_response .= fgets($socket_handler, 128);
    echo $server_response, '<br><br>';

    $start_byte_position = $final_byte_position;
    fclose($socket_handler);
}

该代码实际上有效,除了一件事 - 每个部分都会覆盖以前的内容。 在第一个循环的迭代中,在云上创建100KB文件,在第二次迭代中,这些100KB被另一个100KB替换。在最后一次迭代上传大约28KB,所以最后我在云服务器上有28KB文件而不是整个文件。我究竟做错了什么?我希望这些部分能够互相跟随,而不是替换。

以下是服务器响应,如果这会有所帮助:

  

HTTP / 1.1 201创建日期:2013年8月13日星期二15:11:09 GMT连接:   关闭服务器:Selectel_Storage / 1.0最后修改:2013年8月13日星期二   15:11:09 GMT内容长度:0 etag:ea64231f21c952cdb57a5d3109415d09   content-type:text / html; charset = UTF-8

     

HTTP / 1.1 201创建日期:2013年8月13日星期二15:11:10 GMT连接:   关闭服务器:Selectel_Storage / 1.0最后修改:2013年8月13日星期二   15:11:09 GMT内容长度:0 etag:01ab42f6ad58b401284540d6631dae9d   content-type:text / html; charset = UTF-8

     

HTTP / 1.1 201创建日期:2013年8月13日星期二15:11:10 GMT连接:   关闭服务器:Selectel_Storage / 1.0最后修改:2013年8月13日星期二   15:11:10 GMT内容长度:0 etag:99bd6f0d3cefd75abc140b9359464d6d   content-type:text / html; charset = UTF-8

     

HTTP / 1.1 201创建日期:2013年8月13日星期二15:11:10 GMT连接:   关闭服务器:Selectel_Storage / 1.0最后修改:2013年8月13日星期二   15:11:10 GMT内容长度:0 etag:07cf64ef10e96710b8cb48022ee5dd16   content-type:text / html; charset = UTF-8

     

................................

     

HTTP / 1.1 201创建日期:2013年8月13日星期二15:11:33 GMT连接:   关闭服务器:Selectel_Storage / 1.0最后修改:2013年8月13日星期二   15:11:33 GMT content-length:0 etag:f01e66c96b21665bbbd19ee5e283a4e1   content-type:text / html;字符集= UTF-8

提前致谢!

1 个答案:

答案 0 :(得分:1)

好吧,我终于找到了合适的解决方案。当Marc B响应时,不可能执行部分PUT请求(因为根据定义,这种类型的请求必须是幂等的)。但是,如果我们谈论将文件上传到庞大的云服务器,则可以使用适当的服务器API。据我所知,OpenStack(其Swift组件准确)在文件存储中非常流行。

所以here是简明扼要的官方文件。

我使用的云服务器只允许使用DLO(动态大对象)方法上传大文件,所以您只需要:

1)将大文件拆分成段(或简单地使用fseek和fread)。

2)将这些段发送到一个指定的目录,该名称将进一步被视为整个文件的名称。

3)发送特殊请求(创建所谓的清单文件),将所有下载的段打包在一个文件中。

非常简单!

以下是使用the documentation pageCURL的一个很好的示例:

# First, upload the segments
curl -X PUT -H 'X-Auth-Token: <token>' \
    http://<storage_url>/container/myobject/1 --data-binary '1'
curl -X PUT -H 'X-Auth-Token: <token>' \
    http://<storage_url>/container/myobject/2 --data-binary '2'
curl -X PUT -H 'X-Auth-Token: <token>' \
    http://<storage_url>/container/myobject/3 --data-binary '3'

# Next, create the manifest file
curl -X PUT -H 'X-Auth-Token: <token>' \
    -H 'X-Object-Manifest: container/myobject/' \
    http://<storage_url>/container/myobject --data-binary ''

# And now we can download the segments as a single object
curl -H 'X-Auth-Token: <token>' \
    http://<storage_url>/container/myobject

希望这会对某人有所帮助。

相关问题