YouTube C#API V3,您如何恢复中断的上传?
我无法弄清楚如何在C#YouTube API的V3中恢复中断的上传。
我现有的代码使用V1并且运行正常,但我正在切换到V3。
如果我在不改变任何内容的情况下调用UploadAsync(),它将从头开始。 使用Fiddler,我可以看到这里给出的协议没有被遵循,上传重新开始。
我已尝试按照V1设置流中的位置,但没有可用的ResumeAsync()方法。
Python示例使用NextChunk,但SendNextChunk方法受到保护,在C#中不可用。
在下面的代码中,如果我将它们完成但是上传整个video而不是仅上传其余部分,则UploadVideo()和Resume()都可以正常工作。
如何使用google.apis.youtube.v3恢复中断的上传?
这是我到目前为止尝试过的C#代码。
private ResumableUpload
任何解决方案/建议/演示或“google.apis.youtube.v3”源代码的链接都将非常有用。
提前致谢 !
编辑:新信息
我还在努力,我相信API还没有完成。 无论是那个还是我都错过了一些简单的东西。
我仍然找不到“google.apis.youtube.v3”源代码,所以我下载了最新的“google-api-dotnet-client”源代码。 它包含YouTube API使用的ResumableUpload类。
我成功地通过在UploadAsync()方法中跳过前四行代码来成功继续上传。 我创建了一个名为ResumeAsync()的新方法,它是UploadAsync()的副本,删除了前四行初始化代码。 一切正常,上传从原来的位置恢复完成。
我宁愿不改变API中的代码,所以如果有人知道我应该如何使用它,请告诉我。
我会继续堵塞,看看能否解决这个问题。
这是原始的UploadAsync()方法和我的ResumeAsync()hack。
public async Task UploadAsync(CancellationToken cancellationToken) { try { BytesServerReceived = 0; UpdateProgress(new ResumableUploadProgress(UploadStatus.Starting, 0)); // Check if the stream length is known. StreamLength = ContentStream.CanSeek ? ContentStream.Length : UnknownSize; UploadUri = await InitializeUpload(cancellationToken).ConfigureAwait(false); Logger.Debug("MediaUpload[{0}] - Start uploading...", UploadUri); using (var callback = new ServerErrorCallback(this)) { while (!await SendNextChunkAsync(ContentStream, cancellationToken).ConfigureAwait(false)) { UpdateProgress(new ResumableUploadProgress(UploadStatus.Uploading, BytesServerReceived)); } UpdateProgress(new ResumableUploadProgress(UploadStatus.Completed, BytesServerReceived)); } } catch (TaskCanceledException ex) { Logger.Error(ex, "MediaUpload[{0}] - Task was canceled", UploadUri); UpdateProgress(new ResumableUploadProgress(ex, BytesServerReceived)); throw ex; } catch (Exception ex) { Logger.Error(ex, "MediaUpload[{0}] - Exception occurred while uploading media", UploadUri); UpdateProgress(new ResumableUploadProgress(ex, BytesServerReceived)); } return Progress; } public async Task ResumeAsync(CancellationToken cancellationToken) { try { using (var callback = new ServerErrorCallback(this)) { while (!await SendNextChunkAsync(ContentStream, cancellationToken).ConfigureAwait(false)) { UpdateProgress(new ResumableUploadProgress(UploadStatus.Uploading, BytesServerReceived)); } UpdateProgress(new ResumableUploadProgress(UploadStatus.Completed, BytesServerReceived)); } } catch (TaskCanceledException ex) { UpdateProgress(new ResumableUploadProgress(ex, BytesServerReceived)); throw ex; } catch (Exception ex) { UpdateProgress(new ResumableUploadProgress(ex, BytesServerReceived)); } return Progress; }
这些是显示上传恢复的Fiddler记录 。
经过深思熟虑之后,我决定修改API代码。 我的解决方案保持向后兼容性。
我在下面记录了我的更改,但我不建议使用它们。
在“Google.Apis.Upload”中的ResumableUpload类中的UploadAsync()方法中,我替换了此代码。
BytesServerReceived = 0; UpdateProgress(new ResumableUploadProgress(UploadStatus.Starting, 0)); // Check if the stream length is known. StreamLength = ContentStream.CanSeek ? ContentStream.Length : UnknownSize; UploadUri = await InitializeUpload(cancellationToken).ConfigureAwait(false);
用这个代码
UpdateProgress(new ResumableUploadProgress( BytesServerReceived == 0 ? UploadStatus.Starting : UploadStatus.Resuming, BytesServerReceived)); StreamLength = ContentStream.CanSeek ? ContentStream.Length : UnknownSize; if (UploadUri == null) UploadUri = await InitializeUpload(cancellationToken).ConfigureAwait(false);
我还公开了UploadUri和BytesServerReceived属性。 这允许在ResumableUpload对象被销毁之后或应用程序重新启动之后继续上载。
您只需按常规重新创建ResumableUpload,设置这两个字段并调用UploadAsync()以恢复上载。 在原始上传期间,需要保存这两个字段。
public Uri UploadUri { get; set; } public long BytesServerReceived { get; set; }
我还在IUploadProgress类的UploadStatus枚举中添加了“Resuming”。
public enum UploadStatus { /// /// The upload has not started. /// NotStarted, /// /// The upload is initializing. /// Starting, /// /// Data is being uploaded. /// Uploading, /// /// Upload is being resumed. /// Resuming, /// /// The upload completed successfully. /// Completed, /// /// The upload failed. /// Failed };
开始上传没有任何改变。
如果ResumableUpload Oject和流尚未销毁,请再次调用UploadAsync()以恢复中断的上载。
如果它们已被销毁,请创建新对象并设置UploadUri和BytesServerReceived属性。 原始上传期间可以保存这两个属性。 video详细信息和内容流可以按照正常情况进行配置。
这些更改允许即使在重新启动应用程序或重新启动后也可以恢复上载。 我不确定上传到期前多久,但是当我用我的真实应用程序进行更多测试时,我会报告回来。
为了完整起见,这是我一直在使用的测试代码,在上传过程中多次重启应用程序后,可以很快恢复中断的上传。 恢复和重新启动之间的唯一区别是设置UploadUri和BytesServerReceived属性。
resumableUploadTest = youTubeService.Videos.Insert(video, "snippet,status,contentDetails", fileStream, "video/*"); if (resume) { resumableUploadTest.UploadUri = Settings.Default.UploadUri; resumableUploadTest.BytesServerReceived = Settings.Default.BytesServerReceived; } resumableUploadTest.ChunkSize = ResumableUpload.MinimumChunkSize; resumableUploadTest.ProgressChanged += resumableUpload_ProgressChanged; resumableUploadTest.UploadAsync();
我希望这可以帮助别人。 我花了比预期更长的时间来解决它,我仍然希望我错过了一些简单的事情。 我试图添加自己的error handling程序已经搞砸了很多年,但API会为你完成所有这些。 API确实可以从轻微的短暂打嗝中恢复,但不能从应用程序重启,重启或长时间停机中恢复。
干杯。 米克。
我已经设法使用reflection来实现这一点,并且完全无需修改API。 为了完整起见,我将记录该过程,但不建议这样做。 在可恢复的上传对象中设置私有属性不是一个好主意。
在应用程序重新启动或重新启动后销毁可恢复上载对象时,您仍可以使用Google.Apis.YouTube.v3客户端库的版本“1.8.0.960-rc”恢复上载。
private static void SetPrivateProperty(Object obj, string propertyName, object value) { var propertyInfo = typeof(T).GetProperty(propertyName, BindingFlags.NonPublic | BindingFlags.Instance); if (propertyInfo == null) return; propertyInfo.SetValue(obj, value, null); } private static object GetPrivateProperty (Object obj, string propertyName) { if (obj == null) return null; var propertyInfo = typeof(T).GetProperty(propertyName, BindingFlags.NonPublic | BindingFlags.Instance); return propertyInfo == null ? null : propertyInfo.GetValue(obj, null); }
您需要在ProgressChanged事件期间保存UploadUri。
Upload.ResumeUri = GetPrivateProperty>(InsertMediaUpload, "UploadUri") as Uri;
您需要在调用ResumeAsync之前设置UploadUri和StreamLength。
private const long UnknownSize = -1; SetPrivateProperty>(InsertMediaUpload, "UploadUri", Upload.ResumeUri); SetPrivateProperty>(InsertMediaUpload, "StreamLength", fileStream.CanSeek ? fileStream.Length : Constants.UnknownSize); Task = InsertMediaUpload.ResumeAsync(CancellationTokenSource.Token);
此问题已在Google.Apis.YouTube.v3客户端库的 “1.8.0.960-rc”版本中得到解决。
他们添加了一个名为ResumeAsync的新方法,它运行正常。 我希望我知道他们正在努力。
我需要解决的一个小问题是在重新启动应用程序或重新启动后恢复上载。 当前的api不允许这样做,但是两个小的改动解决了这个问题。
我为ResumeAsync方法添加了一个新签名,该方法接受并设置原始的UploadUri。 需要初始化StreamLength属性以避免溢出错误。
public Task ResumeAsync(Uri uploadUri, CancellationToken cancellationToken) { UploadUri = uploadUri; StreamLength = ContentStream.CanSeek ? ContentStream.Length : UnknownSize; return ResumeAsync(cancellationToken); }
我还公开了UploadUri的getter,因此可以从调用应用程序中保存它。
public Uri UploadUri { get; private set; }