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 和参考链接如下:
- https://developers.google.com/youtube/v3/docs/videos/insert#examples
- https://github.com/youtube/api-samples/tree/master/javascript
解决该问题的主要思路
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){
}
})
我正在编写一个 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 和参考链接如下:
- https://developers.google.com/youtube/v3/docs/videos/insert#examples
- https://github.com/youtube/api-samples/tree/master/javascript
解决该问题的主要思路
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;
这就是我解决此问题的所有解决方案。简单修改源码。
此致,
邓琼.
继续
使用此 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){
}
})