使用file_get_contents上传文件

50
我知道可以使用CURL来轻松完成这个任务,但我想知道是否可以使用http流上下文将文件上传到远程Web服务器,如果可以的话,如何实现?

1
你是不是想说 file_put_contents() 函数? - mellowsoon
不行。尝试使用URL进行file_put_contents操作,你会得到“HTTP wrapper does not support writeable connections”的错误提示。 - Shabbyrobe
1
但是使用 file_get_contents() 函数,您只能下载文件,而不能上传文件! - KingCrunch
你如何使用*file_get_contents()*向流中写入内容? :? - KingCrunch
3
实际上,HTTP流包装器总是向流中写入数据,因为一次HTTP调用同时包含请求(写入)和响应(读取)。 - netcoder
2个回答

101

首先,multipart Content-Type 的第一条规则是定义一个边界,用作分隔每个部分的分界符(因为如其名称所示,它可以有多个部分)。边界可以是 任何不包含在内容体中的字符串。我通常会使用时间戳:

define('MULTIPART_BOUNDARY', '--------------------------'.microtime(true));

一旦您定义了边界,您就必须使用Content-Type头将其发送,以告诉Web服务器应该期望什么分隔符:

$header = 'Content-Type: multipart/form-data; boundary='.MULTIPART_BOUNDARY;

一旦完成了这一步骤,您必须构建一个符合HTTP规范和您发送的标题的适当内容主体。正如您所知,当从表单中POST文件时,通常会有一个表单字段名称。我们将其定义为:

// equivalent to <input type="file" name="uploaded_file"/>
define('FORM_FIELD', 'uploaded_file'); 

然后我们构建内容主体:

$filename = "/path/to/uploaded/file.zip";
$file_contents = file_get_contents($filename);    

$content =  "--".MULTIPART_BOUNDARY."\r\n".
            "Content-Disposition: form-data; name=\"".FORM_FIELD."\"; filename=\"".basename($filename)."\"\r\n".
            "Content-Type: application/zip\r\n\r\n".
            $file_contents."\r\n";

// add some POST fields to the request too: $_POST['foo'] = 'bar'
$content .= "--".MULTIPART_BOUNDARY."\r\n".
            "Content-Disposition: form-data; name=\"foo\"\r\n\r\n".
            "bar\r\n";

// signal end of request (note the trailing "--")
$content .= "--".MULTIPART_BOUNDARY."--\r\n";

正如您所看到的,我们正在使用form-data属性和name参数(表单字段名称)以及filename参数(原始文件名),发送Content-Disposition头。 如果您想要正确填充$_FILES [] ['type'],则还重要发送带有正确MIME类型的Content-Type头。

如果您要上传多个文件,只需为每个文件使用不同的FORM_FIELD,并重复使用$content部分进行该过程。

现在,构建上下文:

$context = stream_context_create(array(
    'http' => array(
          'method' => 'POST',
          'header' => $header,
          'content' => $content,
    )
));

然后执行:

file_get_contents('http://url/to/upload/handler', false, $context);

注意:发送二进制文件之前无需对其进行编码。HTTP 可以很好地处理二进制文件。


2
那个运行得非常好。你能否在你的答案中添加如何使用上传添加额外的帖子字段?此外,只需添加另一个边界,是否可以以这种方式多次执行? - Shabbyrobe
我纠正了一些打字错误。这是一个非常好的解决方案! - Macbric
有人可以解释一下 name=\"foo\"\r\n\r\n"."bar\r\n"; 这部分吗?它不应该只是 foo=bar 吗?我对这部分非常困惑。 - bryan
@bryan:这相当于在“multipart/form-data”格式中的“foo=bar”。 - netcoder
2
如果您要发送的文件非常大,是否有一种分块发送数据的方法? - MacWise
显示剩余6条评论

-1

或者你可以只做:

$postdata = http_build_query(
array(
    'var1' => 'some content',
    'file' => file_get_contents('path/to/file')
)
);

$opts = array('http' =>
    array(
        'method'  => 'POST',
        'header'  => 'Content-Type: application/x-www-form-urlencoded',
        'content' => $postdata
    )
);

$context  = stream_context_create($opts);
$result = file_get_contents('http://example.com/submit.php', false, $context);

你需要把 '/path/to/file' 修改为正确的路径


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接