Exemple #1
0
 /// <summary>
 /// Returns full paths where given file can be stored.
 /// </summary>
 /// <param name="file">A file for which we want to get paths.</param>
 /// <param name="files">A dictionary that maps file id to a file.</param>
 /// <returns>A list of possible paths for a given file.</returns>
 private IEnumerable <string> GetPaths(File file, IDictionary <string, File> files)
 {
     if ((file.Parents == null) || (file.Parents.Count == 0))
     {
         this.Log.LogDebugMessage($"Meta-information does not contain parents for file '{file.Name}',"
                                  + " it seems that it belongs to someone other.");
     }
     else
     {
         foreach (var parent in file.Parents)
         {
             if (files.ContainsKey(parent))
             {
                 var parentPaths = this.GetPaths(files[parent], files);
                 foreach (var path in parentPaths)
                 {
                     yield return(path + Path.DirectorySeparatorChar + FileUtils.SanitizeFileName(file.Name));
                 }
             }
             else
             {
                 yield return(FileUtils.SanitizeFileName(file.Name));
             }
         }
     }
 }
        /// <summary>
        /// Correct creation, modification and access times for a file in local file system to match corresponding values from cloud.
        /// </summary>
        /// <param name="file">Meta-information about file from cloud.</param>
        /// <param name="fileName">Local file name.</param>
        /// <returns>File info with correct time settings.</returns>
        /// <exception cref="SecurityException">The caller does not have the required permission. </exception>
        /// <exception cref="UnauthorizedAccessException">Access to <paramref name="fileName" /> is denied. </exception>
        /// <exception cref="System.IO.IOException"> General I/O exception when trying to modify file times.</exception>
        /// <exception cref="System.IO.DirectoryNotFoundException">The specified path is invalid; for example, it is on
        ///         an unmapped drive.</exception>
        /// <exception cref="PlatformNotSupportedException">The current operating system is not Windows NT or later.</exception>
        /// <exception cref="ArgumentOutOfRangeException">The caller attempts to set an invalid creation time.</exception>
        public static FileInfo CorrectFileTimes(File file, string fileName)
        {
            var fileInfo = new FileInfo(fileName);

            fileInfo.CreationTime   = file.CreatedTime?.ToLocalTime() ?? fileInfo.CreationTime;
            fileInfo.LastWriteTime  = file.ModifiedTime?.ToLocalTime() ?? fileInfo.LastWriteTime;
            fileInfo.LastAccessTime = file.ViewedByMeTime?.ToLocalTime() ?? fileInfo.LastAccessTime;
            return(fileInfo);
        }
        /// <summary>
        /// By given file and a dictionary of directories returns file name on a local file system where this file shall be stored
        /// (without extension for Google Docs files and with extension for binary files).
        /// </summary>
        /// <param name="file">A file for which we want full local file name.</param>
        /// <param name="directories">A dictionary that maps file to a list of directories where it can be stored.</param>
        /// <returns>Full file name (with path) of a file in local file system.</returns>
        /// <exception cref="InternalErrorException">Internal error, trying to save a file without directory info.</exception>
        /// <exception cref="SecurityException">The caller does not have the required permission. </exception>
        /// <exception cref="System.IO.PathTooLongException">The fully qualified path and file name is 260 or more characters.</exception>
        /// <exception cref="ArgumentException">File path contains one or more of the invalid characters.</exception>
        public static string GetFileName(File file, IDictionary <File, IEnumerable <DirectoryInfo> > directories)
        {
            if (!directories.ContainsKey(file))
            {
                throw new InternalErrorException("Internal error, trying to save a file without directory info.");
            }

            var dir = directories[file].FirstOrDefault();

            if (dir == null)
            {
                throw new InternalErrorException("Internal error, trying to save a file without directory info.");
            }

            var idPart = string.Empty;

            // Check for possible names collision, since Google Drive allows to store files with same names in the same directory.
            if (directories.Any(pair => (pair.Value.FirstOrDefault()?.FullName == dir.FullName) &&
                                (pair.Key != file) &&
                                (pair.Key.Name == file.Name)))
            {
                idPart = file.Id;
                idPart = FileUtils.SanitizeFileName(idPart);
                idPart = "." + idPart;
            }

            var fileName = string.Format(
                CultureInfo.InvariantCulture,
                "{0}{1}{2}{3}{4}",
                dir.FullName,
                Path.DirectorySeparatorChar,
                Path.GetFileNameWithoutExtension(FileUtils.SanitizeFileName(file.Name)),
                idPart,
                Path.GetExtension(FileUtils.SanitizeFileName(file.Name)));

            return(fileName);
        }
Exemple #4
0
        /// <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();
            }
        }