最终,我试图让Youtube的样板代码在我的React组件中没有错误地呈现,但是我似乎无法弄清楚。我正在使用is here的样板代码,我正在使用完整的示例(代码上方有一个滑块)。我尝试在外部JS文件中使用JS代码,然后在组件中使用按钮。然后在组件类中使用JS。当我将JS作为单独的文件使用时,出现错误,提示未定义GoogleAuth.signIn()
。因此,我以为Google API加载得不够快,并将脚本更改为HTML的顶部,同样的错误。
有什么建议吗?当我在其中使用示例代码时,即使没有实现React也可以正常运行,我会丢失什么?
组件渲染
<div className="upload">
<div className="container">
<button onClick={this.handleClick}>Authorize</button>
<button >Select file</button>
<input type="file" />
<br />
<br />
<button >Upload file</button>
</div>
</div>
upload.js
/* global gapi */
/***** START BOILERPLATE CODE: Load client library, authorize user. *****/
import $ from "jquery";
// Global variables for GoogleAuth object, auth status.
var GoogleAuth;
var selectedFile;
/**
* Load the API's client and auth2 modules.
* Call the initClient function after the modules load.
*/
function handleClientLoad() {
console.log("HERE!");
gapi.load("client:auth2", initClient);
}
function initClient() {
// Initialize the gapi.client object, which app uses to make API requests.
// Get API key and client ID from API Console.
// 'scope' field specifies space-delimited list of access scopes
gapi.client
.init({
clientId:
"196030380022-es0tk5qapn33r8on5jr5vn4k88l6mkrp.apps.googleusercontent.com",
discoveryDocs: [
"https://www.googleapis.com/discovery/v1/apis/youtube/v3/rest"
],
scope:
"https://www.googleapis.com/auth/youtube.force-ssl https://www.googleapis.com/auth/youtubepartner"
})
.then(function() {
GoogleAuth = gapi.auth2.getAuthInstance();
// Listen for sign-in state changes.
GoogleAuth.isSignedIn.listen(updateSigninStatus);
// Handle initial sign-in state. (Determine if user is already signed in.)
setSigninStatus();
// Call handleAuthClick function when user clicks on "Authorize" button.
$("#select-file-button").click(function() {
$("#select-file").click();
});
$("#upload-file-button").click(function() {
defineRequest();
});
$("#select-file").bind("change", function() {
selectedFile = $("#select-file").prop("files")[0];
});
});
}
export function handleAuthClick(event) {
gapi.load("client:auth2", initClient);
// Sign user in after click on auth button.
GoogleAuth.signIn();
}
let isAuthorized = "";
function setSigninStatus() {
var user = GoogleAuth.currentUser.get();
isAuthorized = user.hasGrantedScopes(
"https://www.googleapis.com/auth/youtube.force-ssl https://www.googleapis.com/auth/youtubepartner"
);
// Toggle button text and displayed statement based on current auth status.
if (isAuthorized) {
defineRequest();
}
}
function updateSigninStatus(isSignedIn) {
setSigninStatus();
}
function createResource(properties) {
var resource = {};
var normalizedProps = properties;
for (var p in properties) {
var value = properties[p];
if (p && p.substr(-2, 2) == "[]") {
var adjustedName = p.replace("[]", "");
if (value) {
normalizedProps[adjustedName] = value.split(",");
}
delete normalizedProps[p];
}
}
for (var p in normalizedProps) {
// Leave properties that don't have values out of inserted resource.
if (normalizedProps.hasOwnProperty(p) && normalizedProps[p]) {
var propArray = p.split(".");
var ref = resource;
for (var pa = 0; pa < propArray.length; pa++) {
var key = propArray[pa];
if (pa == propArray.length - 1) {
ref[key] = normalizedProps[p];
} else {
ref = ref[key] = ref[key] || {};
}
}
}
}
return resource;
}
function removeEmptyParams(params) {
for (var p in params) {
if (!params[p] || params[p] == "undefined") {
delete params[p];
}
}
return params;
}
function executeRequest(request) {
request.execute(function(response) {
console.log(response);
});
}
function buildApiRequest(requestMethod, path, params, properties) {
params = removeEmptyParams(params);
var request;
if (properties) {
var resource = createResource(properties);
request = gapi.client.request({
body: resource,
method: requestMethod,
path: path,
params: params
});
} else {
request = gapi.client.request({
method: requestMethod,
path: path,
params: params
});
}
executeRequest(request);
}
/**
* Retrieve the access token for the currently authorized user.
*/
function getAccessToken(event) {
return GoogleAuth.currentUser.get().getAuthResponse(true).access_token;
}
/**
* Helper for implementing retries with backoff. Initial retry
* delay is 1 second, increasing by 2x (+jitter) for subsequent retries
*
* @constructor
*/
var RetryHandler = function() {
this.interval = 1000; // Start at one second
this.maxInterval = 60 * 1000; // Don't wait longer than a minute
};
console.log(RetryHandler.prototype);
/**
* Invoke the function after waiting
*
* @param {function} fn Function to invoke
*/
RetryHandler.prototype.retry = function(fn) {
setTimeout(fn, this.interval);
this.interval = this.nextInterval_();
};
/**
* Reset the counter (e.g. after successful request.)
*/
RetryHandler.prototype.reset = function() {
this.interval = 1000;
};
/**
* Calculate the next wait time.
* @return {number} Next wait interval, in milliseconds
*
* @private
*/
RetryHandler.prototype.nextInterval_ = function() {
var interval = this.interval * 2 + this.getRandomInt_(0, 1000);
return Math.min(interval, this.maxInterval);
};
/**
* Get a random int in the range of min to max. Used to add jitter to wait times.
*
* @param {number} min Lower bounds
* @param {number} max Upper bounds
* @private
*/
RetryHandler.prototype.getRandomInt_ = function(min, max) {
return Math.floor(Math.random() * (max - min + 1) + min);
};
/**
* Helper class for resumable uploads using XHR/CORS. Can upload any
* Blob-like item, whether files or in-memory constructs.
*
* @example
* var content = new Blob(["Hello world"], {"type": "text/plain"});
* var uploader = new MediaUploader({
* file: content,
* token: accessToken,
* onComplete: function(data) { ... }
* onError: function(data) { ... }
* });
* uploader.upload();
*
* @constructor
* @param {object} options Hash of options
* @param {string} options.token Access token
* @param {blob} options.file Blob-like item to upload
* @param {string} [options.fileId] ID of file if replacing
* @param {object} [options.params] Additional query parameters
* @param {string} [options.contentType] Content-type, if overriding the
* type of the blob.
* @param {object} [options.metadata] File metadata
* @param {function} [options.onComplete] Callback for when upload is complete
* @param {function} [options.onProgress] Callback for status of in-progress
* upload
* @param {function} [options.onError] Callback if upload fails
*/
var MediaUploader = function(options) {
var noop = function() {};
this.file = options.file;
this.contentType =
options.contentType || this.file.type || "application/octet-stream";
this.metadata = options.metadata || {
title: this.file.name,
mimeType: this.contentType
};
this.token = options.token;
this.onComplete = options.onComplete || noop;
this.onProgress = options.onProgress || noop;
this.onError = options.onError || noop;
this.offset = options.offset || 0;
this.chunkSize = options.chunkSize || 0;
this.retryHandler = new RetryHandler();
this.url = options.url;
if (!this.url) {
var params = options.params || {};
params.uploadType = "resumable";
this.url = this.buildUrl_(options.fileId, params, options.baseUrl);
}
this.httpMethod = options.fileId ? "PUT" : "POST";
};
/**
* Initiate the upload.
*/
MediaUploader.prototype.upload = function() {
var self = this;
var xhr = new XMLHttpRequest();
xhr.open(this.httpMethod, this.url, true);
xhr.setRequestHeader("Authorization", "Bearer " + this.token);
xhr.setRequestHeader("Content-Type", "application/json");
xhr.setRequestHeader("X-Upload-Content-Length", this.file.size);
xhr.setRequestHeader("X-Upload-Content-Type", this.contentType);
xhr.onload = function(e) {
if (e.target.status < 400) {
var location = e.target.getResponseHeader("Location");
this.url = location;
this.sendFile_();
} else {
this.onUploadError_(e);
}
}.bind(this);
xhr.onerror = this.onUploadError_.bind(this);
xhr.send(JSON.stringify(this.metadata));
};
/**
* Send the actual file content.
*
* @private
*/
MediaUploader.prototype.sendFile_ = function() {
var content = this.file;
var end = this.file.size;
if (this.offset || this.chunkSize) {
// Only slice the file if we're either resuming or uploading in chunks
if (this.chunkSize) {
end = Math.min(this.offset + this.chunkSize, this.file.size);
}
content = content.slice(this.offset, end);
}
var xhr = new XMLHttpRequest();
xhr.open("PUT", this.url, true);
xhr.setRequestHeader("Content-Type", this.contentType);
xhr.setRequestHeader(
"Content-Range",
"bytes " + this.offset + "-" + (end - 1) + "/" + this.file.size
);
xhr.setRequestHeader("X-Upload-Content-Type", this.file.type);
if (xhr.upload) {
xhr.upload.addEventListener("progress", this.onProgress);
}
xhr.onload = this.onContentUploadSuccess_.bind(this);
xhr.onerror = this.onContentUploadError_.bind(this);
xhr.send(content);
};
/**
* Query for the state of the file for resumption.
*
* @private
*/
MediaUploader.prototype.resume_ = function() {
var xhr = new XMLHttpRequest();
xhr.open("PUT", this.url, true);
xhr.setRequestHeader("Content-Range", "bytes */" + this.file.size);
xhr.setRequestHeader("X-Upload-Content-Type", this.file.type);
if (xhr.upload) {
xhr.upload.addEventListener("progress", this.onProgress);
}
xhr.onload = this.onContentUploadSuccess_.bind(this);
xhr.onerror = this.onContentUploadError_.bind(this);
xhr.send();
};
/**
* Extract the last saved range if available in the request.
*
* @param {XMLHttpRequest} xhr Request object
*/
MediaUploader.prototype.extractRange_ = function(xhr) {
var range = xhr.getResponseHeader("Range");
if (range) {
this.offset = parseInt(range.match(/\d+/g).pop(), 10) + 1;
}
};
/**
* Handle successful responses for uploads. Depending on the context,
* may continue with uploading the next chunk of the file or, if complete,
* invokes the caller's callback.
*
* @private
* @param {object} e XHR event
*/
MediaUploader.prototype.onContentUploadSuccess_ = function(e) {
if (e.target.status == 200 || e.target.status == 201) {
this.onComplete(e.target.response);
} else if (e.target.status == 308) {
this.extractRange_(e.target);
this.retryHandler.reset();
this.sendFile_();
}
};
/**
* Handles errors for uploads. Either retries or aborts depending
* on the error.
*
* @private
* @param {object} e XHR event
*/
MediaUploader.prototype.onContentUploadError_ = function(e) {
if (e.target.status && e.target.status < 500) {
this.onError(e.target.response);
} else {
this.retryHandler.retry(this.resume_.bind(this));
}
};
/**
* Handles errors for the initial request.
*
* @private
* @param {object} e XHR event
*/
MediaUploader.prototype.onUploadError_ = function(e) {
this.onError(e.target.response); // TODO - Retries for initial upload
};
/**
* Construct a query string from a hash/object
*
* @private
* @param {object} [params] Key/value pairs for query string
* @return {string} query string
*/
MediaUploader.prototype.buildQuery_ = function(params) {
params = params || {};
return Object.keys(params)
.map(function(key) {
return encodeURIComponent(key) + "=" + encodeURIComponent(params[key]);
})
.join("&");
};
/**
* Build the upload URL
*
* @private
* @param {string} [id] File ID if replacing
* @param {object} [params] Query parameters
* @return {string} URL
*/
MediaUploader.prototype.buildUrl_ = function(id, params, baseUrl) {
var url = baseUrl;
if (id) {
url += id;
}
var query = this.buildQuery_(params);
if (query) {
url += "?" + query;
}
return url;
};
/***** END BOILERPLATE CODE *****/
function defineRequest() {
var metadata = createResource({
"snippet.categoryId": "22",
"snippet.defaultLanguage": "",
id: "?",
"snippet.description": "Description of uploaded video.",
"snippet.channelId": "",
"snippet.tags[]": "",
"snippet.title": "YAY",
"status.embeddable": "",
"status.license": "",
"status.privacyStatus": "private",
"status.publicStatsViewable": ""
});
var token = getAccessToken();
if (!token) {
alert("You need to authorize the request to proceed.");
return;
}
if (!selectedFile) {
alert("You need to select a file to proceed.");
return;
}
var params = { part: "snippet,status" };
var uploader = new MediaUploader({
baseUrl: "https://www.googleapis.com/upload/youtube/v3/videos",
file: selectedFile,
token: token,
metadata: metadata,
params: params,
onError: function(data) {
var message = data;
try {
var errorResponse = JSON.parse(data);
message = errorResponse.error.message;
} finally {
alert(message);
}
}.bind(this),
onProgress: function(data) {
var currentTime = Date.now();
console.log(
"Progress: " + data.loaded + " bytes loaded out of " + data.total
);
var totalBytes = data.total;
}.bind(this),
onComplete: function(data) {
var uploadResponse = JSON.parse(data);
console.log("Upload complete for video " + uploadResponse.id);
}.bind(this)
});
uploader.upload();
}