Google Drive API V3更新:没有例外,但不会更新

时间:2019-03-05 11:57:07

标签: c# google-api google-drive-api google-api-dotnet-client

我正在尝试使用以下代码更新Google驱动器上的文件:

private async Task UpdateFileAsync(string fullPath, IList<string> parents, string id)
{
    string mimeT = GetMimeType(fullPath);
    Google.Apis.Drive.v3.Data.File file = new Google.Apis.Drive.v3.Data.File();
    file.Name = System.IO.Path.GetFileName(fullPath);
    //file.MimeType = mimeT;
    //file.Parents = parents;

    //using (var fStream = new FileStream(fullPath, FileMode.Open, FileAccess.Read, FileShare.Read, 4096, true))
    //{
    //    byte[] byteArray = new byte[fStream.Length];
    byte[] byteArray = System.IO.File.ReadAllBytes(fullPath);
        //await fStream.ReadAsync(byteArray, 0, (int)fStream.Length);
        using (var stream = new MemoryStream(byteArray))
        {
            var request = _DriveService.Files.Update(file, id, stream, mimeT);// .Create(file, stream, mimeT);
            request.AddParents = string.Join(",", parents);
            var progress = await request.UploadAsync();
            if (progress.Exception != null)
                throw progress.Exception;
        }
    //}
}
private string GetMimeType(string fileName)
{
    string mimeType = "application/unknown";
    string ext = System.IO.Path.GetExtension(fileName).ToLower();
    Microsoft.Win32.RegistryKey regKey = Microsoft.Win32.Registry.ClassesRoot.OpenSubKey(ext);
    if (regKey != null && regKey.GetValue("Content Type") != null)
        mimeType = regKey.GetValue("Content Type").ToString();
    return mimeType;
}

我没有触摸注释行,因此您可以看到我尝试了什么。 该文件是我首先下载的XML巫婆,提取了IdParents,然后在本地进行修改,因此我想对其进行更新。

本地路径fullPath事先来自FileInfo.GetFullPath(),我已经检查了它的存在,并且在运行时使用断点在本地正确更新了该

如您所见,首先我尝试直接在Google MimeType对象中设置ParentsFile,然后阅读有关不可写字段的信息,因此删除了代码。我还尝试了各种流,首先只使用了FileStream,然后尝试了两种在代码中都能看到的方式。

我已经检查了权限,但是我把它们放在这里,以防万一我忘记了一些东西:

private readonly string[] Scopes = {
        SheetsService.Scope.Spreadsheets,
        DriveService.Scope.DriveFile,
        DriveService.Scope.Drive,
        DriveService.Scope.DriveAppdata,
        DriveService.Scope.DriveMetadata };
...
_Credential = await GoogleWebAuthorizationBroker.AuthorizeAsync(
    GoogleClientSecrets.Load(stream).Secrets,
    Scopes,
    "user",
    CancellationToken.None,
    new FileDataStore(credPath, true));

代码运行正常,progress.Exception始终为null,但是文件未更新。我也可以毫无问题地下载和上传文件。

我非常确定这是我放弃的一件事,但似乎我看不到它。我不知道发生了什么。


编辑: 正如DaImTo在评论中所建议的那样,我试图通过这种方式观察上传的进度:

private async Task UpdateFileAsync(string fullPath, IList<string> parents, string id)
{
    try
    {
        string mimeT = GetMimeType(fullPath);
        Google.Apis.Drive.v3.Data.File file = new Google.Apis.Drive.v3.Data.File();
        file.Name = System.IO.Path.GetFileName(fullPath);
        //file.MimeType = mimeT;
        //file.Parents = parents;

        using (var fStream = new FileStream(fullPath, FileMode.Open, FileAccess.Read, FileShare.Read, 4096, true))
        {
            byte[] byteArray = new byte[fStream.Length];
            //byte[] byteArray = System.IO.File.ReadAllBytes(fullPath);
            await fStream.ReadAsync(byteArray, 0, (int)fStream.Length);
            using (var stream = new MemoryStream(byteArray))
            {
                var request = _DriveService.Files.Update(file, id, stream, mimeT);// .Create(file, stream, mimeT);
                request.AddParents = string.Join(",", parents);
                request.ProgressChanged += (Google.Apis.Upload.IUploadProgress prog) =>
                {
                    switch (prog.Status)
                    {
                        case Google.Apis.Upload.UploadStatus.Uploading:
                            {
                                var forget = Log.WriteAsync($"------------- Progreso subida: {prog.BytesSent.ToString()}");
                                break;
                            }
                        case Google.Apis.Upload.UploadStatus.Completed:
                            {
                                var forget = Log.WriteAsync("------------- Upload complete.");
                            //var memData = memStream.ToArray();
                            break;
                            }
                        case Google.Apis.Upload.UploadStatus.Failed:
                            {
                                var forget = Log.WriteAsync("------------- Upload failed.");
                                break;
                            }
                    }
                };
                request.ResponseReceived += (Google.Apis.Drive.v3.Data.File f) =>
                {
                    var forget = Log.WriteAsync($"------------- File uploaded succesfully: {f.Name}");
                };
                var progress = await request.UploadAsync();

                if (progress.Exception != null)
                    throw progress.Exception;
            }
        }
    }
    catch (Exception e)
    {
        e.ShowException();
    }
}

此外,当我序列化要上传的文件并获取文件的本地路径时,此方法之前的方法:

private async Task SerializeAndUpdateDataFileAsync(DriveContaData data, CancellationToken cancelToken)
{
    var path = Path.Combine(System.AppDomain.CurrentDomain.BaseDirectory, Properties.Settings.Default.DriveContaDataName);
    var tmp = Path.Combine(System.AppDomain.CurrentDomain.BaseDirectory, "tmp" + Properties.Settings.Default.DriveContaDataName);

    try
    {
        await SerializeDriveContaDataAsync(
                        tmp,
                        new SerializableProxy_DriveContaData(data));
        if (cancelToken.IsCancellationRequested)
            return;

        System.IO.File.Delete(path);
        System.IO.File.Move(tmp, path);
        await UpdateFileAsync(path, Properties.Settings.Default.MJContaFolderID.Cast<string>().ToList(), Properties.Settings.Default.DriveContaDataId);
        if (cancelToken.IsCancellationRequested)
            return;
        DataFileSavedAndUploaded = true;
    }
    catch (Exception e)
    {
        e.ShowException();
    }
}

日志显示File uploaded succesfullyUpload complete行。


Edit2: 好的,我做了一个新的干净项目,然后复制/修改了代码以仅进行更新。 结果相同,本地文件正确,登录正确,权限正确,尝试更新驱动器上的文件没有任何反应:无异常,无更新。

此类进行更新:

public class GoogleLogin
{
    public GoogleLogin() { _FilesLoader = new DriveFilesLoader(); }

    private const string ApplicationName = "DriveMJConta";
    private UserCredential _Credential;
    private SheetsService _SheetsService;
    private DriveService _DriveService;
    private DriveFilesLoader _FilesLoader;
    private readonly string[] Scopes = {
        SheetsService.Scope.Spreadsheets,
        DriveService.Scope.DriveFile,
        DriveService.Scope.Drive,
        DriveService.Scope.DriveAppdata,
        DriveService.Scope.DriveMetadata };
    private IList<string> _Parents;
    private string _Id;

    public bool IsSigned { get; private set; }

    private async Task SetMJContaAppFolderAsync()
    {
        var files = _FilesLoader.ListFiles(
                _DriveService,
                new DriveFilesLoader.FilesListOptionalParms()
                {
                    Q = @"name = '- DriveContaDataSave.xml' ",
                    Fields = "files(parents, id)"
                });
        _Parents = files.Files[0].Parents;
        _Id = files.Files[0].Id;
    }
    private string GetMimeType(string fileName)
    {
        string mimeType = "application/unknown";
        string ext = System.IO.Path.GetExtension(fileName).ToLower();
        Microsoft.Win32.RegistryKey regKey = Microsoft.Win32.Registry.ClassesRoot.OpenSubKey(ext);
        if (regKey != null && regKey.GetValue("Content Type") != null)
            mimeType = regKey.GetValue("Content Type").ToString();
        return mimeType;
    }

    public async Task GetUserCredentialAsync()
    {
        //MessengerNSpace.Messenger.SendGuidedMessage("GOOGLELOGIN_START");
        try
        {
            var assembly = Assembly.GetExecutingAssembly();
            using (Stream stream = assembly.GetManifestResourceStream("PruebaGoogleDrive.Resources.client_secret.json"))
            {
                string credPath = System.Environment.GetFolderPath(
                    System.Environment.SpecialFolder.Personal);

                if (!IsSigned)
                {
                    _Credential = await GoogleWebAuthorizationBroker.AuthorizeAsync(
                        GoogleClientSecrets.Load(stream).Secrets,
                        Scopes,
                        "user",
                        CancellationToken.None,
                        new FileDataStore(credPath, true));
                    //Log.WriteAsync("Credential file saved to: " + credPath);
                }
                else
                {
                    await GoogleWebAuthorizationBroker.ReauthorizeAsync(
                        _Credential,
                        CancellationToken.None);
                    //Log.WriteAsync("Credential file saved to: " + credPath);
                }
            }

            _DriveService = new DriveService(new BaseClientService.Initializer()
            {
                HttpClientInitializer = _Credential,
                ApplicationName = ApplicationName
            });

            await SetMJContaAppFolderAsync();
        }
        catch(Exception e)
        {
            e.ShowException();
        }

        MessageBox.Show("Login OK");
    }
    public async Task UpdateFileAsync(string fullPath)
    {
        try
        {
            string mimeT = GetMimeType(fullPath);
            Google.Apis.Drive.v3.Data.File file = new Google.Apis.Drive.v3.Data.File();
            file.Name = System.IO.Path.GetFileName(fullPath);
            //file.MimeType = mimeT;
            //file.Parents = parents;

            using (var fStream = new FileStream(fullPath, FileMode.Open, FileAccess.Read, FileShare.Read, 4096, true))
            {
                byte[] byteArray = new byte[fStream.Length];
                //byte[] byteArray = System.IO.File.ReadAllBytes(fullPath);
                await fStream.ReadAsync(byteArray, 0, (int)fStream.Length);
                using (var stream = new MemoryStream(byteArray))
                {
                    var request = _DriveService.Files.Update(file, _Id, stream, mimeT);// .Create(file, stream, mimeT);
                    request.AddParents = string.Join(",", _Parents);
                    request.ProgressChanged += (Google.Apis.Upload.IUploadProgress prog) =>
                    {
                        switch (prog.Status)
                        {
                            case Google.Apis.Upload.UploadStatus.Uploading:
                                {
                                    MessageBox.Show($"------------- Progreso subida: {prog.BytesSent.ToString()}");
                                    break;
                                }
                            case Google.Apis.Upload.UploadStatus.Completed:
                                {
                                    MessageBox.Show("------------- Upload complete.");
                                    //var memData = memStream.ToArray();
                                    break;
                                }
                            case Google.Apis.Upload.UploadStatus.Failed:
                                {
                                    MessageBox.Show("------------- Upload failed.");
                                    break;
                                }
                        }
                    };
                    request.ResponseReceived += (Google.Apis.Drive.v3.Data.File f) =>
                    {
                        MessageBox.Show($"------------- File uploaded succesfully: {f.Name}");
                    };
                    var progress = await request.UploadAsync();

                    if (progress.Exception != null)
                        throw progress.Exception;
                }
            }
        }
        catch (Exception e)
        {
            e.ShowException();
        }
    }
}

我本来打算创建一个concol项目,但是我错过了单击并创建了WPF项目...没关系,只做了两个按钮,并在代码后面放了这个:

public partial class MainWindow : Window
{
    public MainWindow()
    {
        InitializeComponent();
    }

    private GoogleLogin gl;

    private void Login_Click(object sender, RoutedEventArgs e)
    {
        gl = new GoogleLogin();
        Task.Run(() => gl.GetUserCredentialAsync());
    }

    private void Update_Click(object sender, RoutedEventArgs e)
    {
        Task.Run(() => gl.UpdateFileAsync(System.IO.Path.Combine(System.AppDomain.CurrentDomain.BaseDirectory, "- DriveContaDataSave.xml")));
    }
}

DriveFilesLoader是DaImTo类。我使用它(并在此处复制/粘贴)只是为了加快速度:

public class DriveFilesLoader
{
    public class FilesListOptionalParms
    {
        /// 

        /// The source of files to list.
        /// 
        public string Corpora { get; set; }
        /// 

        /// A comma-separated list of sort keys. Valid keys are 'createdTime', 'folder', 'modifiedByMeTime', 'modifiedTime', 'name', 'quotaBytesUsed', 'recency', 'sharedWithMeTime', 'starred', and 'viewedByMeTime'. Each key sorts ascending by default, but may be reversed with the 'desc' modifier. Example usage: ?orderBy=folder,modifiedTime desc,name. Please note that there is a current limitation for users with approximately one million files in which the requested sort order is ignored.
        /// 
        public string OrderBy { get; set; }
        /// 

        /// The maximum number of files to return per page.
        /// 
        public int? PageSize { get; set; }
        /// 

        /// The token for continuing a previous list request on the next page. This should be set to the value of 'nextPageToken' from the previous response.
        /// 
        public string PageToken { get; set; }
        /// 

        /// A query for filtering the file results. See the "Search for Files" guide for supported syntax.
        /// 
        public string Q { get; set; }
        /// 

        /// A comma-separated list of spaces to query within the corpus. Supported values are 'drive', 'appDataFolder' and 'photos'.
        /// 
        public string Spaces { get; set; }
        /// 

        /// Selector specifying a subset of fields to include in the response.
        /// 
        public string Fields { get; set; }
        /// 

        /// Alternative to userIp.
        /// 
        public string QuotaUser { get; set; }
        /// 

        /// IP address of the end user for whom the API call is being made.
        /// 
        public string UserIp { get; set; }
    }

    /// 

    /// Lists or searches files. 
    /// Documentation https://developers.google.com/drive/v3/reference/files/list
    /// Generation Note: This does not always build corectly.  Google needs to standardise things I need to figuer out which ones are wrong.
    /// 
    /// Authenticated drive service.  
    /// Optional paramaters.        /// FileListResponse
    public Google.Apis.Drive.v3.Data.FileList ListFiles(DriveService service, FilesListOptionalParms optional = null)
    {
        try
        {
            // Initial validation.
            if (service == null)
                throw new ArgumentNullException("service");

            // Building the initial request.
            var request = service.Files.List();
            if(optional != null)
                ApplyOptionalParameters(ref request, optional);
            // Applying optional parameters to the request.                
            request = (FilesResource.ListRequest)ApplyOptionalParms(request, optional);
            // Requesting data.
            return request.Execute();
        }
        catch (Exception ex)
        {
            throw new Exception("Request Files.List failed.", ex);
        }
    }

    private void ApplyOptionalParameters(ref FilesResource.ListRequest request, FilesListOptionalParms parms)
    {
        if(!string.IsNullOrEmpty(parms.Corpora))
            request.Corpora = parms.Corpora;
        if(!string.IsNullOrEmpty(parms.OrderBy))
            request.OrderBy = parms.OrderBy;
        if (parms.PageSize.HasValue)
            request.PageSize = parms.PageSize;
        if (!string.IsNullOrEmpty(parms.PageToken))
            request.PageToken = parms.PageToken;
        if (!string.IsNullOrEmpty(parms.Q))
            request.Q = parms.Q;
        if (!string.IsNullOrEmpty(parms.Spaces))
            request.Spaces = parms.Spaces;
        if (!string.IsNullOrEmpty(parms.Fields))
            request.Fields = parms.Fields;
        if (!string.IsNullOrEmpty(parms.QuotaUser))
            request.QuotaUser = parms.QuotaUser;
        if (!string.IsNullOrEmpty(parms.UserIp))
            request.UserIp = parms.UserIp;
    }

    /// 

    /// Using reflection to apply optional parameters to the request.  
    /// 
    /// If the optonal parameters are null then we will just return the request as is.
    /// 
    /// The request. 
    /// The optional parameters. 
    /// 
    public object ApplyOptionalParms(object request, object optional)
    {
        if (optional == null)
            return request;

        System.Reflection.PropertyInfo[] optionalProperties = (optional.GetType()).GetProperties();

        foreach (System.Reflection.PropertyInfo property in optionalProperties)
        {
            // Copy value from optional parms to the request.  They should have the same names and datatypes.
            System.Reflection.PropertyInfo piShared = (request.GetType()).GetProperty(property.Name);
            if (property.GetValue(optional, null) != null) // TODO Test that we do not add values for items that are null
                piShared.SetValue(request, property.GetValue(optional, null), null);
        }

        return request;
    }
}

还有Exception扩展名,即使有人想复制/粘贴以避免编译错误并尝试使用它本身,

public static class ExceptionExtensions
{
    public static void ShowException(this Exception e, string additionalMessagePrefix = "")
    {
        var msg = $@"{additionalMessagePrefix}
            Error: 
{e.Message} ;

            Trace: 
{e.StackTrace} ;";

        Exception innerEx = e.InnerException;

        while(innerEx != null)
        {
            msg = msg + $@"

            InnerException: 
{(e.InnerException != null ? innerEx.Message : "")} ; 

            InnerException Trace:
{(e.InnerException != null ? innerEx.StackTrace : "")} ;";

            innerEx = innerEx.InnerException;
        }

        System.Windows.MessageBox.Show(msg);
    }
}

2 个答案:

答案 0 :(得分:0)

尝试将文件ID添加到对象。

var file = new Google.Apis.Drive.v3.Data.File();
file.Name = System.IO.Path.GetFileName(fullPath);
file.Id = id;

答案 1 :(得分:0)

Drive API不会引发上载异常。这是您获得实际错误的方法:

示例代码:

var progress = request.Upload();

if (progress.Exception != null)
{
   //Log execption, or break here to debug
   YourLoggingProvider.Log(progress.Exception.Message.ToString());
}
相关问题