/// <summary> /// Downloads Google Docs file. /// </summary> /// <param name="file">Meta-information about file to download.</param> /// <param name="context">Provides context for task execution, like logger object and /// cancellation token.</param> /// <returns>An information about downloaded file.</returns> public async Task <DownloadedFile> Run(File file, ITaskContext context) { if (!this.exportFormats.ContainsKey(file.MimeType) || (this.exportFormats[file.MimeType].Count == 0)) { context.Log.LogError($"Unknown export format for file MIME type {file.MimeType}."); return(null); } // Just take first available export format for now. var exportFormat = this.exportFormats[file.MimeType][0]; var exportRequest = this.service.Files.Export(file.Id, exportFormat); await this.semaphore.WaitAsync(); context.Log.LogDebugMessage( $"Starting to download '{file.Name}' Google Docs file, export format is {exportFormat}."); try { var resultStream = await GoogleRequestHelper.Execute( exportRequest.ExecuteAsStreamAsync, context.CancellationToken, GoogleRequestHelper.Cooldown *Utils.Constants.DownloadThreadsCount); var mimeParts = exportFormat.Split('/', '+'); var extension = mimeParts[mimeParts.Length - 1]; switch (extension) { case "vnd.openxmlformats-officedocument.presentationml.presentation": extension = "pptx"; break; case "xml": extension = "svg"; break; } var fileName = FileDownloadUtils.GetFileName(file, this.directories) + "." + extension; using (var fileStream = new FileStream(fileName, FileMode.Create)) { await resultStream.CopyToAsync(fileStream, 4096, context.CancellationToken); } var fileInfo = FileDownloadUtils.CorrectFileTimes(file, fileName); context.Log.LogDebugMessage( $"File '{file.Name}' downloading finished, saved as '{fileInfo.FullName}'."); return(new DownloadedFile(file, fileInfo.FullName)); } catch (Exception e) { if (context.CancellationToken.IsCancellationRequested) { throw; } context.Fail($"File '{file.Name}' downloading failed.", e); throw; } finally { this.semaphore.Release(); } }
/// <summary> /// Downloads binary (non-"Google Docs") file. /// </summary> /// <param name="service">Google Drive service used to download a file.</param> /// <param name="file">Meta-information about file to download.</param> /// <param name="semaphore">Semaphore used to throttle downloading process.</param> /// <param name="unitsOfWork">Abstract units of work assigned to download this file. Used in progress reporting.</param> /// <param name="directories">Dictionary that contains possible directories to save a file.</param> /// <returns>Information about downloaded file, null if downloading failed.</returns> /// <exception cref="InternalErrorException">Something is wrong with downloader code itself.</exception> public async Task <DownloadedFile> DownloadBinaryFile( DriveService service, File file, AsyncSemaphore semaphore, int unitsOfWork, IDictionary <File, IEnumerable <DirectoryInfo> > directories) { this.Info = file.Name; this.Estimation = unitsOfWork; if (file.Size.HasValue && (file.Size.Value == 0)) { try { var fileName = FileDownloadUtils.GetFileName(file, directories); System.IO.File.Create(fileName); this.Done(); return(new DownloadedFile(file, fileName)); } catch (Exception e) { this.Log.LogError($"Saving zero-length file '{file.Name}' error.", e); this.StatusAccumulator.FailureOccurred(); throw; } } var request = service.Files.Get(file.Id); request.MediaDownloader.ProgressChanged += downloadProgress => { switch (downloadProgress.Status) { case DownloadStatus.NotStarted: break; case DownloadStatus.Downloading: this.Progress = (int)(downloadProgress.BytesDownloaded * unitsOfWork / (file.Size ?? 1)); break; case DownloadStatus.Completed: this.Progress = this.Estimation; break; case DownloadStatus.Failed: this.Progress = this.Estimation; if (!this.CancellationToken.IsCancellationRequested) { this.Status = Status.Error; this.Log.LogError($"Downloading file '{file.Name}' error.", downloadProgress.Exception); this.StatusAccumulator.FailureOccurred(); } break; default: throw new InternalErrorException("DownloadStatus enum contains unknown value."); } }; await semaphore.WaitAsync(); this.Log.LogDebugMessage($"Starting to download '{file.Name}' binary file."); try { var fileName = FileDownloadUtils.GetFileName(file, directories); using (var fileStream = new FileStream(fileName, FileMode.Create)) { await GoogleRequestHelper.Execute( ct => request.DownloadAsync(fileStream, ct), this.CancellationToken, GoogleRequestHelper.Cooldown *Utils.Constants.DownloadThreadsCount); } var fileInfo = FileDownloadUtils.CorrectFileTimes(file, fileName); this.Log.LogDebugMessage($"File '{file.Name}' downloading finished, saved as {fileInfo.FullName}."); this.StatusAccumulator.SuccessItem(); this.Done(); return(new DownloadedFile(file, fileInfo.FullName)); } catch (Exception e) { if (this.CancellationToken.IsCancellationRequested) { throw; } this.Log.LogError($"Downloading file '{file.Name}' error.", e); this.StatusAccumulator.FailureOccurred(); throw; } finally { semaphore.Release(); } }