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); 
相關問題