【问题标题】:Getting a video from S3 and Uploading to YouTube in PHP从 S3 获取视频并用 PHP 上传到 YouTube
【发布时间】:2012-04-23 05:40:43
【问题描述】:

我有一些代码可以将视频文件上传到 YouTube:

$yt = new Zend_Gdata_YouTube($httpClient);

// create a new VideoEntry object
$myVideoEntry = new Zend_Gdata_YouTube_VideoEntry();

// create a new Zend_Gdata_App_MediaFileSource object
$filesource = $yt->newMediaFileSource('file.mov');
$filesource->setContentType('video/quicktime');
// set slug header
$filesource->setSlug('file.mov');

我在 S3 中有视频,我想将它们上传到 YouTube。我们 S3 帐户中的视频是公开的,所以我可以使用 wget 之类的命令。在运行此脚本 (shell_exec("wget ".$s3videoURL)) 之前,我是否应该运行 wgets 视频文件并将其下载到本地的命令?

或者我应该尝试输入 MediaFileSource 作为 S3 文件本身的 URL?

主要是,我只需要稳定性(不是经常超时的解决方案);速度和本地存储并不重要(视频文件上传后我可以在本地删除)。

最好的方法是什么?

谢谢!

更新:我应该提一下,这个脚本每次执行将上传大约 5 个视频到 YouTube。

【问题讨论】:

    标签: php upload youtube amazon-s3


    【解决方案1】:

    这是一个老问题,但我相信我有更好的答案。

    您不必将视频写入 HDD,也不能将整个内容保存在 RAM 中(我假设它是一个大文件)。

    您可以使用 PHP AWS SDK 和 Google 客户端库从 S3 缓冲文件并将其发送到 YouTube。使用 registerStreamWrapper 方法将 S3 注册为文件系统并使用来自 YouTube API 的可恢复上传。然后你所要做的就是用 fread 从 S3 读取块并将它们发送到 YouTube。这样您甚至可以限制 RAM 的使用。

    我假设您从 Google_Video 类创建了视频对象(代码中的 $video)。这是一个完整的代码。

    <?php
    require_once 'path/to/libraries/aws/vendor/autoload.php';
    require_once 'path/to/libraries/google-client-lib/autoload.php';
    
    use Aws\S3\S3Client;
    
    $chunkSizeBytes = 2 * 1024 * 1024; // 2 mb
    $streamName = 's3://bucketname/video.mp4';
    
    $s3client = S3Client::factory(array(
                        'key'    => S3_ACCESS_KEY,
                        'secret' => S3_SECRET_KEY,
                        'region' => 'eu-west-1' // if you need to set.
                    ));
    $s3client->registerStreamWrapper();
    
    $client = new Google_Client();
    $client->setClientId(YOUTUBE_CLIENT_ID);
    $client->setClientSecret(YOUTUBE_CLIENT_SECRET);
    $client->setAccessToken(YOUTUBE_TOKEN);
    
    $youtube = new Google_YoutubeService($client);
    $media = new Google_MediaFileUpload('video/*', null, true, $chunkSizeBytes);
    
    $filesize = filesize($streamName); // use it as a reguler file.
    $media->setFileSize($filesize);
    
    $insertResponse = $youtube->videos->insert("status,snippet", $video, array('mediaUpload' => $media));
    $uploadStatus = false;
    
    $handle = fopen($streamName, "r");
    $totalReceived = 0;
    $chunkBuffer = '';
    while (!$uploadStatus && !feof($handle)) {
        $chunk = fread($handle, $chunkSizeBytes);
        $chunkBuffer .= $chunk;
        $chunkBufferSize = strlen($chunkBuffer);
        if($chunkBufferSize > $chunkSizeBytes) {
            $fullChunk = substr($chunkBuffer, 0, $chunkSizeBytes);
            $leapChunk = substr($chunkBuffer, $chunkSizeBytes);
            $uploadStatus = $media->nextChunk($insertResponse, $fullChunk);
            $totalSend += strlen($fullChunk);
    
            $chunkBuffer = $leapChunk;
            echo PHP_EOL.'Status: '.($totalReceived).' / '.$filesize.' (%'.(($totalReceived / $filesize) * 100).')'.PHP_EOL;
        }
    
        $totalReceived += strlen($chunk);
    }
    
    $extraChunkLen = strlen($chunkBuffer);
    $uploadStatus = $media->nextChunk($insertResponse, $chunkBuffer);
    $totalSend += strlen($chunkBuffer);
    fclose($handle);
    

    【讨论】:

    • 谢谢。你节省了我的时间:)。
    • 以防万一有人在尝试简化此操作时遇到问题,由于远程位置的 php 中 fread 的限制,您需要将块附加到块缓冲区。来自 php.net:“警告当从任何非常规本地文件读取时,例如读取远程文件或从 popen() 和 fsockopen() 时返回的流,读取将在数据包可用后停止。这意味着您应该像下面的示例中所示的那样以块的形式收集数据。”
    【解决方案2】:

    “MediaFileSource”必须是真实文件。它不需要 URL,因此您需要先将视频从 S3 复制到您的服务器,然后再将它们发送到 YouTube。

    如果您的使用量很少,您可能可以使用“shell_exec”,但出于各种原因,使用Zend S3 ServicecURL 从 S3 中提取文件可能会更好。

    【讨论】:

    • 非常感谢您的回答,我将探索这些替代方案
    【解决方案3】:

    我必须对@previous_developer 的答案进行一些更改,以使其与 Youtube Data API V3 一起使用(请为他投票,因为除了他的之外我找不到任何工作代码)。

    $streamName = 's3://BUCKET-NAME/VIDEO.mp4';
    
    
    /**
    Since I have been using Yii 2. Use the AWS 
    SDK directly instead.
    */
    
        $aws = Yii::$app->awssdk->getAwsSdk();
        $s3client = $aws->createS3();
    
    
        $s3client->registerStreamWrapper();
    
    
        $service = new \Google_Service_YouTube($client);
    
        $snippet = new \Google_Service_YouTube_VideoSnippet();
        $snippet->setTitle("Test title");
        $snippet->setDescription("Test descrition");
        $snippet->setTags(array("tag1","tag2"));
        $snippet->setCategoryId("22");
    
        $status = new \Google_Service_YouTube_VideoStatus();
        $status->privacyStatus = "public";
    
        $video = new \Google_Service_YouTube_Video();
        $video->setSnippet($snippet);
        $video->setStatus($status);
    
        $client->setDefer(true);
        $insertResponse = $service->videos->insert("status,snippet", $video);
    
    
        $media = new MediaFileUpload(
            $client,
            $insertResponse,
            'video/*',
            null,
            true,
            false
        );
    
        $filesize = filesize($streamName); // use it as a reguler file.
        $media->setFileSize($filesize);
    
    
        $chunkSizeBytes = 2 * 1024 * 1024; // 2 mb
    
        $uploadStatus = false;
    
        $handle = fopen($streamName, "r");
        $totalSend = 0;
        $totalReceived = 0;
        $chunkBuffer = '';
        while (!$uploadStatus && !feof($handle)) {
            $chunk = fread($handle, $chunkSizeBytes);
            $chunkBuffer .= $chunk;
            $chunkBufferSize = strlen($chunkBuffer);
            if($chunkBufferSize > $chunkSizeBytes) {
                $fullChunk = substr($chunkBuffer, 0, $chunkSizeBytes);
                $leapChunk = substr($chunkBuffer, $chunkSizeBytes);
                $uploadStatus = $media->nextChunk($fullChunk);
                $totalSend += strlen($fullChunk);
    
                $chunkBuffer = $leapChunk;
                echo PHP_EOL.'Status: '.($totalReceived).' / '.$filesize.' (%'.(($totalReceived / $filesize) * 100).')'.PHP_EOL;
            }
    
            $totalReceived += strlen($chunk);
        }
    
        $extraChunkLen = strlen($chunkBuffer);
        $uploadStatus = $media->nextChunk($chunkBuffer);
        $totalSend += strlen($chunkBuffer);
        fclose($handle);
    
    
    
        // If you want to make other calls after the file upload, set setDefer back to false
        $client->setDefer(false);
    

    【讨论】:

      【解决方案4】:

      $chunkSizeBytes = 2 * 1024 * 1024; // 2 mb

          $s3client = $this->c_aws->getS3Client();
          $s3client->registerStreamWrapper();
      
          try {
      
              $client = new \Google_Client();
      
              $client->setAccessType("offline");
              $client->setApprovalPrompt('force');
      
              $client->setClientId(GOOGLE_CLIENT_ID);
              $client->setClientSecret(GOOGLE_CLIENT_SECRET);
              $token = $client->fetchAccessTokenWithRefreshToken(GOOGLE_REFRESH_TOKEN);
      
      
              $client->setAccessToken($token);
      
              $youtube = new \Google_Service_YouTube($client);
      
              // Create a snippet with title, description, tags and category ID
              // Create an asset resource and set its snippet metadata and type.
              // This example sets the video's title, description, keyword tags, and
              // video category.
              $snippet = new \Google_Service_YouTube_VideoSnippet();
              $snippet->setTitle($title);
              $snippet->setDescription($summary);
              $snippet->setTags(explode(',', $keywords));
      
              // Numeric video category. See
              // https://developers.google.com/youtube/v3/docs/videoCategories/list
      

      // $sn-p->setCategoryId("22");

              // Set the video's status to "public". Valid statuses are "public",
              // "private" and "unlisted".
              $status = new \Google_Service_YouTube_VideoStatus();
              $status->privacyStatus = "public";
      
      
              // Associate the snippet and status objects with a new video resource.
              $video = new \Google_Service_YouTube_Video();
              $video->setSnippet($snippet);
              $video->setStatus($status);
      
              // Setting the defer flag to true tells the client to return a request which can be called
              // with ->execute(); instead of making the API call immediately.
              $client->setDefer(true);
      
              $insertRequest = $youtube->videos->insert("status,snippet", $video);
      
              $media = new \Google_Http_MediaFileUpload(
                  $client,
                  $insertRequest,
                  'video/*',
                  null,
                  true,
                  $chunkSizeBytes
              );
      
              $result = $this->c_aws->getAwsFile($aws_file_path);
      
              $media->setFileSize($result['ContentLength']);
      
              $uploadStatus = false;
      
              // Seek to the beginning of the stream
              $result['Body']->rewind();
      
              // Read the body off of the underlying stream in chunks
              while (!$uploadStatus && $data = $result['Body']->read($chunkSizeBytes)) {
      
                  $uploadStatus = $media->nextChunk($data);
      
              }
              $client->setDefer(false);
              if ($uploadStatus->status['uploadStatus'] == 'uploaded') {
                  // Actions to perform for a successful upload
                   $uploaded_video_id = $uploadStatus['id'];
                  return ($uploadStatus['id']);
              }
          }catch (\Google_Service_Exception $exception){
              return '';
              print_r($exception);
          }
      

      【讨论】:

        猜你喜欢
        • 2015-04-21
        • 1970-01-01
        • 2011-10-12
        • 2017-06-19
        • 1970-01-01
        • 2021-02-10
        • 1970-01-01
        • 2017-04-13
        • 2020-11-05
        相关资源
        最近更新 更多