3

我无法解决如何在C#YouTube API的V3中恢复被中断的上传。YouTube C#API V3,您如何恢复中断的上传?

我现有的代码使用V1和正常工作,但我切换到V3。

如果我在不更改任何内容的情况下调用UploadAsync(),它将从头开始。使用Fiddler,我可以看到没有遵循protocol given here,上传重新开始。

我试着根据V1设置流内的位置,但没有ResumeAsync()方法可用。

Python示例使用NextChunk,但SendNextChunk方法受保护,并且在C#中不可用。

在下面的代码中,UploadVideo()和Resume()工作正常,如果我让他们完成,但整个视频上传,而不是其他部分。

如何使用google.apis.youtube.v3恢复中断上传?

这是我到目前为止尝试过的C#代码。

private ResumableUpload<Video> UploadVideo(
    YouTubeService youTubeService, Video video, Stream stream, UserCredential userCredentials) 
{ 
    var resumableUpload = youTubeService.Videos.Insert(video, 
     "snippet,status,contentDetails", stream, "video/*"); 
    resumableUpload.OauthToken = userCredentials.Token.AccessToken; 
    resumableUpload.ChunkSize = 256 * 1024; 
    resumableUpload.ProgressChanged += resumableUpload_ProgressChanged; 
    resumableUpload.ResponseReceived += resumableUpload_ResponseReceived;     
    resumableUpload.UploadAsync(); 
    return resumableUpload; 
} 

private void Resume(ResumableUpload<Video> resumableUpload) 
{ 
    //I tried seeking like V1 but it doesn't work 
    //if (resumableUpload.ContentStream.CanSeek) 
    // resumableUpload.ContentStream.Seek(resumableUpload.ContentStream.Position, SeekOrigin.Begin); 

    resumableUpload.UploadAsync(); // <----This restarts the upload        
} 

void resumableUpload_ResponseReceived(Video obj) 
{     
    Debug.WriteLine("Video status: {0}", obj.Status.UploadStatus);      
} 

void resumableUpload_ProgressChanged(IUploadProgress obj) 
{ 
    Debug.WriteLine("Position: {0}", (resumableUploadTest == null) ? 0 : resumableUploadTest.ContentStream.Position); 
    Debug.WriteLine("Status: {0}", obj.Status); 
    Debug.WriteLine("Bytes sent: {0}", obj.BytesSent); 
} 

private void button2_Click(object sender, EventArgs e) 
{ 
    Resume(resumableUploadTest); 
} 

任何解决方案/建议/演示或指向“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<IUploadProgress> 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<IUploadProgress> 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 records

回答

2

经过一番考虑后,我决定修改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枚举中添加了“恢复”。

public enum UploadStatus 
{ 
    /// <summary> 
    /// The upload has not started. 
    /// </summary> 
    NotStarted, 

    /// <summary> 
    /// The upload is initializing. 
    /// </summary> 
    Starting, 

    /// <summary> 
    /// Data is being uploaded. 
    /// </summary> 
    Uploading, 

    /// <summary> 
    /// Upload is being resumed. 
    /// </summary> 
    Resuming, 

    /// <summary> 
    /// The upload completed successfully. 
    /// </summary> 
    Completed, 

    /// <summary> 
    /// The upload failed. 
    /// </summary> 
    Failed 
}; 

开始上传没有任何改变。

如果ResumableUpload Oject和流没有被销毁,再次调用UploadAsync()以恢复中断的上载。

如果它们已被销毁,请创建新对象并设置UploadUri和BytesServerReceived属性。这两个属性可以在原始上传期间保存。视频详情和内容流可以按照正常进行配置。

这几个更改允许即使重新启动应用程序或重新启动后仍可以恢复上载。我不确定在上传过期之前多久,但当我使用真实应用程序完成了更多测试时,我会报告回来。

为了完整起见,这是我一直在使用的测试代码,在上载过程中多次重新启动应用程序后,它可以愉快地恢复中断上载。恢复和重新启动之间的唯一区别是设置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<Video>.MinimumChunkSize; 
resumableUploadTest.ProgressChanged += resumableUpload_ProgressChanged; 
resumableUploadTest.UploadAsync(); 

我希望这可以帮助别人。我花了比预期更多的时间来解决这个问题,我仍然希望我错过了一些简单的事情。我搞砸了很长时间试图添加我自己的错误处理程序,但API为你做了这一切。该API确实可以从轻微短暂的打嗝中恢复,但不会从应用程序重新启动,重新启动或长时间停机恢复。

干杯。米克。

1

此问题已在Google.Apis.YouTube.v3 Client Library的“1.8.0.960-rc”版本中解决。

他们已经添加了一种名为ResumeAsync的新方法,它工作正常。我希望我知道他们正在努力。

我需要解决的一个小问题是在重新启动应用程序或重新启动后恢复上载。目前的API不允许这样做,但是两个小小的改变解决了这个问题。

我为ResumeAsync方法添加了一个新签名,该方法接受并设置原始UploadUri。 StreamLength属性需要被初始化以避免溢出错误。

public Task<IUploadProgress> ResumeAsync(Uri uploadUri, CancellationToken cancellationToken) 
{ 
    UploadUri = uploadUri; 
    StreamLength = ContentStream.CanSeek ? ContentStream.Length : UnknownSize; 
    return ResumeAsync(cancellationToken); 
} 

我还暴露了UploadUri的getter,因此它可以从调用应用程序保存。

public Uri UploadUri { get; private set; } 
+0

现在,Google API v3 .NET客户端库包含一个功能,用于在ResumableUpload期间保存UploadUri,并在稍后使用UploadUri在程序重新启动时恢复上载。 https://github.com/google/google-api-dotnet-client-samples中有两个ResumableUpload示例 –

2

我已经设法使用反射来实现这个功能,并避免了根本不需要修改API。为了完整起见,我将记录这个过程,但不推荐。在可恢复的上传对象中设置私有属性并不是一个好主意。

当应用程序重新启动或重新启动后,可恢复的上载对象被销毁时,仍然可以使用Google.Apis.YouTube.v3 Client Library版本“1.8.0.960-rc”恢复上载。

private static void SetPrivateProperty<T>(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<T>(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<ResumableUpload<Video>>(InsertMediaUpload, "UploadUri") as Uri; 

您需要在调用ResumeAsync之前设置UploadUri和StreamLength。

private const long UnknownSize = -1; 
SetPrivateProperty<ResumableUpload<Video>>(InsertMediaUpload, "UploadUri", Upload.ResumeUri); 
SetPrivateProperty<ResumableUpload<Video>>(InsertMediaUpload, "StreamLength", fileStream.CanSeek ? fileStream.Length : Constants.UnknownSize); 
Task = InsertMediaUpload.ResumeAsync(CancellationTokenSource.Token); 
相关问题