Я пытаюсь обновить файл на диске 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-файл, который я сначала загружаю, извлекая Id
и Parents
, а затем изменяю локально, поэтому я хочу обновить его.
Локальный путь fullPath
заранее взят из FileInfo.GetFullPath()
и я проверил, что он существует и корректно обновляется локально во время выполнения с точкой останова.
Как вы можете видеть, сначала я попытался установить MimeType
и Parents
непосредственно в объекте google File
Затем я прочитал о недоступных для записи полях, поэтому я удалил эту часть кода.Я также пробовал с различными потоками, сначала я использовал только 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
всегда равен нулю, но файл не обновляется.Я также могу загружать и загружать файлы без проблем.
И снова я почти уверен, что это мелочь, которую я забыл, но опять же кажется, что я не могу ее увидеть.Я не знаю, что происходит.
.
Редактировать: По предложению 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 succesfully
и Upload 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);
}
}