首页 > 解决方案 > Google Drive API V3 更新:不例外但不更新

问题描述

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

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 女巫,获取Idand Parents,然后在本地修改,所以我想更新它。

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

如您所见,首先我尝试直接在 google对象中进行设置MimeType,然后我阅读了有关不可写字段的信息,因此我删除了那部分代码。我也尝试过各种流,首先我只使用了,然后我尝试了你可以在代码中看到的两种方式。ParentsFileFileStream

我已经检查了权限,但我把它们放在这里以防我忘记了什么:

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始终为空,但文件没有得到更新。我也可以毫无问题地下载和上传文件。

再说一次,我很确定这是我忘记的一件小事,但我又似乎看不到它。我不知道发生了什么。

.


编辑: 正如 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();
        }
    }
}

我打算制作一个 concole 项目,但我错过了点击并制作了一个 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);
    }
}

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

解决方案


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

示例代码:

var progress = request.Upload();

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

推荐阅读