【问题标题】:Youtube upload API and cordova / phonegapYoutube上传API和cordova/phonegap
【发布时间】:2015-03-29 14:31:51
【问题描述】:

我正在编写一个允许用户录制视频并通过他们的 api 将其上传到 youtube 的 cordova 应用程序。

如果我使用文件输入并通过

访问文件

$('#file').get(0).files[0]

我收到一个file 对象,可以毫无问题地上传。

如果我录制视频,我会收到一个 medialist 对象。 然后我可以拨打window.resolveLocalFileSystemURL( video[0].localURL , success, fail);

在成功回调中,我收到一个文件列表对象。也不接受。

在这个filelistobject 上,我可以调用data.file(success,fail),它最终返回给我一个文件对象。但是在尝试上传到 youtube 时出现 308 错误。

我怀疑这与访问本地文件路径的权限有关。如果有人有这方面的经验,我很想听听解决方法。

这里是上传代码:

UploadVideo.prototype.ready = function(accessToken, video) {
    this.accessToken = accessToken;
    this.gapi = gapi;
    this.authenticated = true;
    $this = this;

function result(data){
    function success(data){

        data.name = "VID_20150329_160037.mp4";
        console.log(data)
        $this.uploadFile( data );
    }
    data.file(success)

}
function fail(data){
    console.log(data)
}
window.resolveLocalFileSystemURL( video[0].localURL , result, fail);


//this.uploadFile( $('#file').get(0).files[0] );

// $('#button').on("click", this.handleUploadClicked.bind(this));

【问题讨论】:

  • en.wikipedia.org/wiki/HTTP_302 302 错误是重定向。这听起来像是指向不推荐使用的端点,或者身份验证无效并从端点重新路由。
  • 但它正在使用这个文件对象 - $('#file').get(0).files[0]。所以它必须是关于文件编码的东西
  • 嗨,这已经有一段时间了,但我目前正在开发一个小型应用程序,也尝试在录制视频后进行 Youtube 上传,尽管我正在使用角度/离子。您是否有机会对此进行回购以将其视为示例?
  • 如何获取访问令牌?由于谷歌禁止在 HTTP/HTTPS 之外加载的政策,gapi.client.init 在 Cordova 中加载时永远不会返回请参阅:developers.google.com/api-client-library/javascript/start/…

标签: cordova meteor youtube-api youtube-javascript-api


【解决方案1】:

使用这篇博文,我设法让元数据和文件上传正常工作: http://lithostech.com/2013/10/upload-google-youtube-api-v3-cors/

通过在发送之前注销 FileTransfer 标头信息,我可以看到格式不同:

Content-Disposition: form-data; name="part"
{"snippet":{"title":"Video title","description":"Video description","tags":"Video tags","categoryId":22},"status":{"privacyStatus":"unlisted"}}

应该是:

Content-Disposition: form-data; name=""; filename="file.json"
Content-Type: application/json
{"snippet":{"title":"Video title","description":"Video description","tags":"Video tags","categoryId":22},"status":{"privacyStatus":"unlisted"}}

这是一个包含工作元数据的更新版本:

function uploadVideo(fileURL) {
    var options = new FileUploadOptions();
    options.fileKey = 'file';
    options.fileName = fileURL.substr(fileURL.lastIndexOf('/') + 1);
    options.mimeType = 'video/mpg';
    options.chunkedMode = false;
    options.headers = {
        Authorization: 'Bearer ' + accessToken
    };
    options.params = {
        "": {
            snippet: {
                title: 'Video title',
                description: 'Video description',
                tags: 'Video tags',
                categoryId: 22
            },
            status: {
                privacyStatus: 'unlisted'
            }
        }
    };
    var ft = new FileTransfer();
    ft.upload(fileURL, 'https://www.googleapis.com/upload/youtube/v3/videos?part=snippet,status', function (data) {
        console.log('upload success', data);
    }, function (e) {
        console.log('upload error', e);
    }, options, true);
    ft.onprogress = function (progressEvent) {
        console.log('onprogress: ' + ((progressEvent.loaded / progressEvent.total) * 100) + '%');
    };
}

请注意,我也稍微修改了 FileTransfer 插件:

文件传输.java 第 374 - 376 行

beforeData.append("Content-Disposition: form-data; name=\"").append(key.toString()).append("\";");
beforeData.append(" filename=\"").append("file.json").append('"').append(LINE_END);
beforeData.append("Content-Type: ").append("application/json").append(LINE_END).append(LINE_END);

修改cordova/phonegap/ionic 中的插件后,您需要重新加载它。我通过删除平台并再次添加它来做到这一点:

cordova platform remove android; cordova platform add android;

【讨论】:

  • 那么这只适用于Android吗?还是 IOS 开箱即用。
  • 我只在 Android 上试过这个,我想你可能需要修改 iOS FileTransfer 插件来支持 Content Headers
  • 我收到一个错误,意外的部分 sn-p 和状态。我创建了一个带有更改 github.com/westonganger/cordova-plugin-file-transfer 的分支
  • 你能注销它创建的结果吗?我手头没有 iOS 设备!可能是我将名称留空: Content-Disposition: form-data;名称="";文件名="file.json"
  • 我用你的代码测试安卓版本,不是IOS。
【解决方案2】:

我终于让它工作了。我需要使用 cordova 文件传输插件

这里是代码

function postVideo(accessToken, fileURI) {
  var metadata = {
    snippet: {
      title: "test",
      description: "test",
      tags: ["youtube-cors-upload"],
      categoryId: 21
    },
    status: {
      privacyStatus: "unlisted"
    }
  }

  var options = new FileUploadOptions();
  options.fileKey = "file";
  options.fileName = 'test';
  options.mimeType = "video/mp4";
  options.chunkedMode = false;

  options.headers = {
    Authorization: "Bearer " + accessToken,
    "Access-Control-Allow-Origin": "http://meteor.local"
  };

  var params = new Object();
  params.part = Object.keys(metadata).join(',')

  options.params = params;
  console.log(options)
  var ft = new FileTransfer();
  ft.upload(fileURI, "https://www.googleapis.com/upload/youtube/v3/videos?part=snippet", win, fail, options, true);

  ft.onprogress = function(progressEvent) {
    if (progressEvent.lengthComputable) {
      // console.log(progressEvent)
      // loadingStatus.setPercentage(progressEvent.loaded / progressEvent.total);
    } else {
      console.log('something not loading')
        // loadingStatus.increment();
    }
    console.log(progressEvent.loaded / progressEvent.total);
  };
}

function win(r) {
  console.log(r)
  console.log("Code = " + r.responseCode);
  console.log("Response = " + r.response);
  console.log("Sent = " + r.bytesSent);
}

function fail(error) {
  console.log(error)
    // alert("An error has occurred: Code = " + error.code);
  console.log("upload error source " + error.source);
  console.log("upload error target " + error.target);
}

【讨论】:

  • 这太好了,你知道如何传递正确的元数据了吗?当我使用这个例子时,标题是未知的
  • 抱歉我还没有测试标题。只好把这个项目搁置一旁。最好的办法是检查请求中是否正在发送数据。如果不是谷歌 YouTube api 帮助将是最好的查找位置。抱歉,我目前无法提供更多帮助
  • 查看我的更新答案,带有元数据的 youtube 上传工作正常!!
  • 如何使用 java 脚本从刷新令牌生成访问令牌?你能提供代码来做到这一点吗?
【解决方案3】:

感谢您分享信息。

当我想使用cordova插件在设备中上传选定的文件时,我遇到了同样的情况。

我的情况不是308错误,而是cordova-file插件返回的文件数据格式与输入文件HTML的Blob格式不正确。

因此,有一个简单的解决方案来解决问题。它是将文件数据转换为输入文件HTML的右Blob格式。

我使用了来自 Youtube Data API 的示例和如下参考链接:

解决这个问题的主要思路

1) 将文件数据转换为输入文件 HTML 的 Blob 格式。
2) 修改 upload_video.js 文件的内容。
3) 修改 cors_upload.js 文件内容。

解决方案详情

1) 将文件数据转换为输入文件HTML的Blob格式:

○ 输入内容是cordova-file插件返回的文件
○ 使用 FileReader HTML5 将文件内容读取为 ArrayBuffer。
○ 使用 Blob 对象创建带有数据转换的新内容。

*转换数据示例代码

UploadVideo.prototype.convertToBlob = function (file, onSuccess) {
          var reader = new FileReader();
          reader.onload = (function (event) {
            var blob = new Blob([new Uint8Array(this.result)], { type: file.type });
            onSuccess(blob);
          });
          reader.readAsArrayBuffer(file);
        };


2) 修改upload_video.js文件内容:

○ 为存储所选文件的 UploadVideo 对象创建“myFile”属性。
○ 检索cordova-file 插件返回的数据内容,并将其转换为Blob 格式。

*读取文件内容并将内容转换为 Blob 示例代码

 var self_ = this; //the instance of UploadVideo object
        window.resolveLocalFileSystemURL(fileUri, function (fileEntry) {
              //success
              fileEntry.file(function(file){
                   //Store selected file to re-use when upload event
            self_.myFile = file;
            //We could use file as input of  convertToBlob ()
            self_.convertToBlob (file, function(blob){
                self_.myFile.newBlob  = blob;
            });
              });
          }, function (response) {
              //error
          });

○ 需要确保新的 Blob 内容已经用于在 UploadVideo.prototype.handleUploadClicked() 方法中输入 this.uploadFile()。

*修改handleUploadClicked()中的源代码

代替: this.uploadFile($('#file').get(0).files[0]); 通过新: this.uploadFile(this.myFile);



3) 修改 cors_upload.js 文件内容:

○ 通过 MediaUploader.prototype.sendFile_() 方法修改新的上传内容

*修改sendFile_()中的源代码

代替: var 内容 = this.file; 通过新: var 内容 = this.file.newBlob;



这就是我解决此问题的全部解决方案。只需对源代码进行简单修改。

最好的问候,
党庆。

【讨论】:

    【解决方案4】:

    继续 Kim T's answer,下面是如何让它在 iOS 上运行:

    使用来自此pull request 的代码修改位于xCode 项目的Plugins 目录中的CDVFileTransfer.m 文件。修改如下:

    src/ios/CDVFileTransfer.m

    @@ -25,6 +25,7 @@ Licensed to the Apache Software Foundation (ASF) under one
    +#import <Foundation/NSJSONSerialization.h>
    
    @@ -203,6 +204,11 @@ - (NSURLRequest*)requestForUploadCommand:(CDVInvokedUrlCommand*)command fileData
    +        // if it is a valid json object get the NSString representation
     +        if ([NSJSONSerialization isValidJSONObject:val]){
     +            NSData *jsonData = [NSJSONSerialization dataWithJSONObject:val options:NSJSONWritingPrettyPrinted error:nil];
     +            val = [[NSString alloc] initWithData:jsonData encoding:NSUTF8StringEncoding];
     +        }
    

    然后在同一个文件中,在 requestForUploadCommand 函数中,当它迭代 options 中的 key 时,更改此行:

    [postBodyBeforeFile appendData:[[NSString stringWithFormat:@"Content-Disposition: form-data; name=\"%@\"\r\n\r\n", key] dataUsingEncoding:NSUTF8StringEncoding]];
    

    到这里:

    [postBodyBeforeFile appendData:[[NSString stringWithFormat:@"Content-Disposition: form-data; name=\"%@\"; filename=\"file.json\"\r\nContent-Type: application/json\r\n\r\n", key] dataUsingEncoding:NSUTF8StringEncoding]];
    

    这可能没关系,但我在使用上述拉取请求中的代码时将options:NSJSONWritingPrettyPrinted 更改为options:0

    一旦我做出这些更改,在 xCode 中重新构建项目并在我妻子的 iPad 上启动它,带有元标记信息的视频上传到 YouTube 就像一个魅力。

    【讨论】:

      【解决方案5】:

      接受的答案是错误的并且不起作用。正确的答案是您应该使用此处详细介绍的 2 部分可恢复上传方法: https://developers.google.com/youtube/v3/guides/using_resumable_upload_protocol

      示例:

        var videoResourceYT = //yt video resource goes here
        }
      
        videoResourceYT = JSON.stringify(videoResourceYT)
        var ftoptions = new FileUploadOptions();
        var ft = new FileTransfer();
        $.ajax({
          url:"https://www.googleapis.com/upload/youtube/v3/videos?uploadType=resumable&part=snippet,status",
          method:"POST",
          data: videoResourceYT,
          contentType:"application/json; charset=UTF-8",
              beforeSend: function (request) {
                    request.setRequestHeader("Authorization", "Bearer " + token);
                    request.setRequestHeader("X-Upload-Content-Length", /*file length, not actually needed*/);
                    request.setRequestHeader("X-Upload-Content-Type", "video/*");
              },
              success: function(ytuploadData, ytTextStatus, ytRequest){
                var nextUrlYT = ytRequest.getResponseHeader('Location');
                console.log(nextUrlYT)
      
                ftoptions.mimeType = "video/*";
                ftoptions.chunkedMode = false;
                ftoptions.headers = {
                    "Authorization": 'Bearer ' + token,
                    "Content-Type": "video/*"
                };
                ftoptions.httpMethod = "PUT"
      
      
                //?part=snippet,status
                ft.upload(/*options go here*/);
              },
              error: function(e1,e2,e3){
              }
        })
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2018-02-08
        • 2015-11-30
        • 2014-11-16
        • 2015-02-03
        • 1970-01-01
        • 1970-01-01
        • 2014-02-14
        • 2017-03-19
        相关资源
        最近更新 更多