我正在使用PHP通过直接上传到Youtube发送视频。它适用于较小尺寸的视频,但在尝试发送390 MB视频时,我收到以下错误:
PHP致命错误:内存不足 (已分配3932160)(试图分配 390201902字节)
我尝试增加memory_limit
,但这没有帮助。
if ($isFile) {
ini_set('memory_limit', '2G')
$data = file_get_contents($data);
}
$ch = curl_init($url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($ch, CURLOPT_POST, 1);
curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
curl_setopt($ch, CURLOPT_POSTFIELDS, $data);
$out = curl_exec($ch);
curl_close($ch);
return $out;
我也试过通过exec()
运行卷曲,但是甚至更奇怪的事情发生了:
卷曲 http://uploads.gdata.youtube.com/feeds/api/users/default/uploads -H'POST / feeds / api / users / default / uploads HTTP / 1.1'-H'主机: uploads.gdata.youtube.com'-H '授权:OAuth [snip oauth info]“'-H'GData-Version:2'-H 'X-GData-Client:www.mywebsite.com' -H'X-GData-Key:key = [snip]'-H'Slug:video.AVI'-H'Content-Type: 多部分/相关; boundary =“iUI5C0hzisAHkx9SvaRJ”' - H '内容长度:390193710'-H '连接:关闭'-d /tmp/youtube.xml
/tmp/youtube.xml是我保存要上传的数据文件的地方。也许这种用法是错误的?
这将需要大约6分钟,所以看起来文件正在发送但是我得到一个空的回复:
% Total % Received % Xferd Average Speed Time Time Time Current
Dload Upload Total Spent Left Speed
0 0 0 0 0 0 0 0 --:--:-- 0:00:01 --:--:-- 0
0 0 0 0 0 0 0 0 --:--:-- 0:00:02 --:--:-- 0
...
0 0 0 0 0 0 0 0 --:--:-- 0:06:00 --:--:-- 0
curl: (52) Empty reply from server
编辑:
我正在使用OAuth,所以我不能使用普通的PHP API库。我必须上传一个XML文件,其中包含XML文件中包含的视频二进制数据outlined here
我found another person with the same problem提供了代码来读取并以块的形式发送文件。但是,在尝试此方法时,Youtube将返回411页,说“Content-Length”标题是必需的。我正在设置内容长度标题,所以这可能是一个错误。此方法使用fsockopen()
函数而不是cURL。 [实际上,再次查看代码,我意识到我只是用“\ n”而不是“\ r \ n”来分隔标题。这可能是个问题。我会尝试回车]
编辑2:
我认为“\ r \ n”有效,但现在有了代码,我再次收到Youtube的回复。
那里有任何卷曲专家可以帮我搞定这个吗?我完全被这个难过了。
答案 0 :(得分:3)
尝试在发送之前不要将整个文件读入内存。卷曲恕我直言,支持在上传之前阅读文件,因此应该在内存边框内工作。有关示例,请参阅以下博客文章:http://dtbaker.com.au/random-bits/uploading-a-file-using-curl-in-php.html
我还没有使用youtube直接上传API,但经过快速浏览后,我发现这似乎不是正常的html表单文件上传,而是更复杂的数据格式。我不确定你是否可以用普通的cURL做这个,而不用自己在内存中构建整个POST数据。
如果您有这么小的内存限制(大约4 MB的RAM),您可以尝试在PHP的流API之上构建自己的简单HTTP客户端:创建一个临时文件并将您的POST请求数据写入该文件句柄(使用fwrite()表示普通字符串,使用stream_copy_to_stream()表示直接文件到文件的数据传输。请求准备就绪后,将临时文件回滚到开头,然后将该流复制到与youtube http服务器的连接中(再次使用stream_copy_to_stream())。
由于流是以小块的形式复制的,因此即使对于大型文件,也应该能够使用少于4 MB的RAM进行复制。
编辑:
遵循伪php-code-mashup应该会有所帮助;)
$xmlAPIRequest = /* fill with the XML-API-Request */
$boundaryString = /* fill me with some random data */
// create temporary file handle
$pdh = tmpfile();
fwrite($pdh, "--$boundaryString\r\n");
fwrite($pdh, "Content-Type: application/atom+xml; charset=UTF-8\r\n\r\n");
fwrite($pdh, $xmlAPIRequest."\r\n");
fwrite($pdh, "--$boundaryString\r\n");
fwrite($pdh, "Content-Type: <video_content_type>\r\nContent-Transfer-Encoding: binary\r\n\r\n");
$videoFile = fopen("/path/to/video", "r");
stream_copy_to_stream($videoFile, $pdh);
fclose($videoFile);
fwrite($pdh, "--$boundaryString--\r\n");
/* not quite sure, whether there needs to be another linebreak before the boundary string */
$info = fstat($pdh);
rewind($pdh);
$contentLength = $info['size'];
$conn = fsockopen("hostname", 80);
/* write http request to $conn and use $contentLength for Content-Length header */
/* after last header you put another line break to tell them that now the body follows */
// write post data from stream to stream
stream_copy_to_stream($pdh, $conn);
// ... process response... etc...
这段代码肯定存在很多错误,但由于它只是一个简短的例子,我认为我们可以忍受这一点。 ;)
答案 1 :(得分:0)
尝试做:
ini_set('memory_limit', -1);
有效吗?
答案 2 :(得分:0)
怎么样
$filename = "--REMOTE FILE--";
$localfile = "/storage/local.flv";
$handle = fopen($filename, "r");
while ($contents = fread($handle, 10485760)) { // thats 10 MB
$localhandle = fopen($localfile, "a");
fwrite ($localhandle, $contents);
fclose($localhandle);
}
fclose($handle);
答案 3 :(得分:0)
这是我用来实现这个的代码:
public function uploadVideo(Model_Row_Video $video)
{
$request = $this->getOAuthRequest(self::URL_UPLOAD, 'POST');
$boundary = 'RANDOM BOUNDARY STRING';
$videoFile = $video->getAbsolutePath();
$contentType = $this->getContentType($videoFile);
$xml = $this->getAtom($video);
$data = <<<EOD
--{$boundary}
Content-Type: application/atom+xml; charset=UTF-8
{$xml}
--{$boundary}
Content-Type: {$contentType}
Content-Transfer-Encoding: binary\r\n\r\n
EOD;
$footer = "\r\n--{$boundary}--\r\n";
$pdh = tmpfile();
fwrite($pdh, $data);
$f_video = fopen($videoFile, "r");
stream_copy_to_stream($f_video, $pdh);
fclose($f_video);
fwrite($pdh, $footer);
$info = fstat($pdh);
$headers = array(
"POST /feeds/api/users/default/uploads HTTP/1.1",
'Host: uploads.gdata.youtube.com',
$request->to_header(),
'GData-Version: 2',
'X-GData-Client: ' . self::CONSUMER_KEY,
'X-GData-Key: key=' . self::DEVELOPER_KEY,
'Slug: ' . $video['local'],
'Content-Type: multipart/related; boundary="' . $boundary . '"',
'Content-Length: ' . $info['size'],
'Connection: close'
);
$headers_str = implode("\r\n", $headers) . "\r\n\r\n";
rewind($pdh);
$conn = fsockopen('uploads.gdata.youtube.com', 80, $errno, $errstr, 30);
fputs($conn, $headers_str);
stream_copy_to_stream($pdh, $conn);
$return = '';
while (!feof($conn)) {
$return .= fgets($conn, 128);
}
fclose($conn);
echo "errno: $errno\n";
echo "errstr: $errstr\n";
$out = strstr($return, '<?xml');
$xml = simplexml_load_string($out);
if (!$xml) {
echo $out;
}
$xml->registerXPathNamespace('yt','http://gdata.youtube.com/schemas/2007');
$id = $xml->xpath('//yt:videoid');
return $id[0];
}