Youtube 上传 API 和 cordova / phonegap

Youtube upload API and cordova / phonegap

我正在编写一个 cordova 应用程序,让用户可以录制视频并通过他们的 api.

将其上传到 youtube

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

访问文件

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

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

如果我录制视频,我会收到媒体对象。 然后我可以打电话给 window.resolveLocalFileSystemURL( video[0].localURL , success, fail);

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

在这个 filelist 对象上我可以调用 data.file(success,fail) 最后 returns 我是一个文件对象。但是在尝试上传到 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));

我终于让它工作了。我需要使用 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);
}

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

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

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 插件:

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;

感谢您分享信息。

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

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

这样,就有了解决问题的简单方法。它将文件数据转换为输入文件 HTML.

的正确 Blob 格式

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

解决该问题的主要思路

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

解决方案详情

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

○ 输入内容为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()中的源码

Replace:
   this.uploadFile($('#file').get(0).files[0]);
By new:
   this.uploadFile(this.myFile);



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

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

*修改sendFile_()中的源码

Replace:
   var content = this.file;
By new:
   var content = this.file.newBlob;



这就是我解决此问题的所有解决方案。简单修改源码。

此致,
邓琼.

继续 ,以下是如何让它在 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 非常有效。

接受的答案是错误的,不起作用。正确答案是您应该使用此处详细介绍的 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){
        }
  })