2010-01-24 71 views
3

我使用PHP通过直接上传将视频发送到Youtube。它工作正常的更小尺寸的影片,但试图发送一个390 MB的视频时,我得到以下错误:通过Youtube API上传大视频导致内存不足

PHP Fatal error: Out of memory (allocated 3932160) (tried to allocate 390201902 bytes)

我试过增加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()试图运行卷曲后来甚至怪异的事情发生了:

curl http://uploads.gdata.youtube.com/feeds/api/users/default/uploads -H 'POST /feeds/api/users/default/uploads HTTP/1.1' -H 'Host: uploads.gdata.youtube.com' -H 'Authorization: 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: multipart/related; boundary="iUI5C0hzisAHkx9SvaRJ"' -H 'Content-Length: 390193710' -H 'Connection: close' -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

I found another person with the same problem谁提供了读取和发送文件的代码块。但是,当尝试使用这种方法时,Youtube将返回一个411页面,表示需要“Content-Length”标题。我正在设置内容长度标题,所以这可能是一个错误。此方法使用fsockopen()函数而不是cURL。 [其实,再次查看代码,我意识到我只是用“\ n”而不是“\ r \ n”来分隔标题。这可能是问题。我会尝试用回车以及]

编辑2:

我觉得“\ r \ n”个工作,但现在的代码,我再次收到从Youtube空回复。

任何卷曲专家在那里,可以帮助我得到这个工作?我完全被这个难住了。

回答

3

尽量不要在发送之前阅读整个文件到内存中。 curll恕我直言,在上传之前支持读取文件本身,因此应该在内存边界内工作。请参阅以下博客文章中的示例:http://dtbaker.com.au/random-bits/uploading-a-file-using-curl-in-php.html

我没有用YouTube直接上传API还没有工作,但咋一看后,我看到了,这似乎并没有成为一个普通的HTML表单文件上传,但多一点复杂的数据格式。我不确定你是否可以用简单的cURL来做到这一点,而无需自己在内存中构建整个POST数据。

如果你有这么小的内存限制(~4MB的RAM),你可以尝试在PHP的流API之上构建你自己的简单HTTP客户端:创建一个临时文件并将你的POST请求数据写入该文件句柄(对于普通字符串使用fwrite(),对于直接文件到文件数据传输使用stream_copy_to_stream()。当您的请求准备好时,将临时文件倒回到开头,然后将该流复制到与youtube http服务器的连接中(再次使用stream_copy_to_stream())。

由于流被复制成小块,所以即使对于大文件,也应该能够使用少于4 MB的RAM来完成此操作。

编辑:

以下伪PHP代码,混搭应该是有帮助的;)

$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... 

当然有很多在这个代码中的错误,但是,因为它只是一个简单的例子,我认为我们可以忍受这一点。 ;)

+0

)感谢您的回复,我编辑了我的问题以提供更多信息。 – 2010-01-25 16:17:54

+0

美丽!它的工作原理!我想我得到了空的答复,因为我没有在最终边界之后的“\ r \ n”。打开了这个问题的赏金,并授予你编辑你的答案和提供代码。我需要一段时间才能找出它。 – 2010-01-28 07:35:08

+2

@MattMcCormick你会发布你的完整代码吗?我正在尝试上传一个很大的文件,但不能得到它的工作 – J888 2013-06-09 09:21:13

0

尝试做:

ini_set('memory_limit', -1); 

是否行得通?

+0

我已经试过了。它不起作用。我也尝试在php.ini中设置它。我正在使用可能有256 MB内存限制的VPS。这可能是我得到内存不足错误的原因。这就是为什么我想直接尝试使用CURL,但是这对我来说也不适用。 – 2010-01-24 07:53:16

+0

@Matt McCormick:这就解释了......恐怕我现在不能有任何帮助。 =( – 2010-01-24 07:58:24

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); 
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]; 
} 
相关问题