private void SetTotalsTexts() { ArchiveSizeText = Properties.Resources.TotalArchiveSizeText + LongToSizeString.ConvertToString((double)this._archiveSize) + ", "; this.TotalFilesToArchiveText = this._archiveInfo.Count + (this._archiveInfo.Count == 1 ? " file. " : " files. "); if (_excludedFiles.Count > 0) { this.TotalFilesToArchiveText = this.TotalFilesToArchiveText.Replace('.', ','); this.TotalFilesToExcludeText = this._excludedFiles.Count + (this._excludedFiles.Count == 1 ? " file" : " files") + " will not be uploaded."; } }
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) { double size = System.Convert.ToDouble(value); return(LongToSizeString.ConvertToString(size)); }
/// <summary> /// Prepare the ArchiveFileInfo list needed for this archive. This method scans for all files /// to be included in the archive and prepares their keynames, file paths and base prefixes. /// If the user selection doesn't include any files or if the user selection has a total size /// more than the remaining free Deepfreeze Storage, this method throws an exception. /// </summary> /// <param name="paths"></param> /// <returns></returns> private async Task PrepareArchivePathsAndSizeAsync(IEnumerable <string> paths, SelectionMode selectionMode) { // Clear list with archive files info. this._archiveInfo.Clear(); // Clear errors ErrorSelectingFiles = null; IsReset = false; IsBusy = true; BusyMessageText = Properties.Resources.CalculatingTotalArchiveSizeText; try { long size = 0; // split paths to files and folders. var files = new List <string>(); var directories = new List <string>(); bool isDirectoryRestricted; foreach (var p in paths) { FileAttributes attr = File.GetAttributes(p); // if the selected folder is indeed a directory and not a junction point // add it for archiving. if ((attr & FileAttributes.Directory) == FileAttributes.Directory && (attr & FileAttributes.ReparsePoint) != FileAttributes.ReparsePoint) { isDirectoryRestricted = CheckIfDirectoryIsRestricted(p); if (!isDirectoryRestricted) { if (directories.Contains(p)) { continue; } directories.Add(p); } else { this._excludedFiles.Add(p, Enumerations.FileCategory.RestrictedDirectory); } } else { if (files.Contains(p)) { continue; } files.Add(p); } } // If the initial selection includes just one folder, // then set the archive's title to its name. (step 1) if (directories.Count == 1 && files.Count == 0) { this.ArchiveTitle = new DirectoryInfo(directories.First()).Name; } // The directories list may contain subdirectories of some of its elements. // We need to remove them because we need to add them later on in the subDirectories dictionary // so we can get the correct grandparent value. var dirsToRemove = new List <string>(); foreach (var dir in directories) { var dirInfo = new DirectoryInfo(dir); var subDirs = dirInfo.GetDirectories().Select(x => x.FullName); foreach (var sub in subDirs) { if (directories.Contains(sub) && !dirsToRemove.Contains(sub)) { dirsToRemove.Add(sub); } } dirInfo = null; subDirs = null; } foreach (var dirToRemove in dirsToRemove) { directories.Remove(dirToRemove); } dirsToRemove.Clear(); dirsToRemove = null; var subDirectories = new Dictionary <string, string>(); // Okay now find all the subdirectories to include respecting restrictions. foreach (var dir in directories) { var subsWithoutJunctions = await IgnoreJunctionsUnderPath(dir); foreach (var sub in subsWithoutJunctions.OrderBy(x => x)) { if (subDirectories.Keys.Contains(sub)) { continue; } // for each subdirectory to include we create a key with the dir's parent as a value. // we will need this to find the prefix to remove for the key names. subDirectories.Add(sub, Directory.GetParent(dir).FullName + "\\"); } subsWithoutJunctions.Clear(); subsWithoutJunctions = null; } // add all found subdirectories in the directories list // which will be scanned for files at the top level for each directory. if (subDirectories.Count > 0) { foreach (var sub in subDirectories.Keys) { if (directories.Contains(sub)) { continue; } directories.Add(sub); } } // REMARK // We want to handle directories first. The main reason behind this is that in search results // one file may be contained more than one time. If a descendant directory of this file is contained // in the results, then we want the file to be included with a key name indicating this hierarchy. // The "clean" selection of the file from the search results, will have to be ignored. // // Example: C:\test\test.txt // Searching for 'test' will return two results, the test folder and the test.txt file. // Selecting both, will add the same path two times. But the folder result will get included // first in the _archiveInfo list, and when the code tries to add the 'clean // for all directories selected for archiving, add all files in _archiveFileInfo. foreach (var dir in directories) { // Fetch all files in directory, only those on the top level. var dirFiles = Directory.GetFiles(dir, "*", SearchOption.TopDirectoryOnly); if (dirFiles.Count() > 0) { await Task.Run(() => { foreach (var f in dirFiles) { if (this._archiveInfo.Select(x => x.FilePath).Contains(f)) { continue; } var fileCategory = Utilities.CheckFileApiRestrictions(f); if (fileCategory != Enumerations.FileCategory.Normal) { this.HasInvalidFiles = true; this._excludedFiles.Add(f, fileCategory); continue; } var info = new FileInfo(f); // Check that the archive size does not exceed the maximum allowed file size. // S3 supports multipart uploads with up to 10000 parts and 5 TB max size. // Since DF supports part size of 5 MB, archive size must not exceed 5 MB * 10000 if (info.Length > MAX_ALLOWED_FILE_SIZE) { throw new Exception("The file " + f + " exceeds the maximum allowed archive size of " + LongToSizeString.ConvertToString((double)MAX_ALLOWED_FILE_SIZE) + "."); } var baseToRemove = (!subDirectories.Keys.Contains(dir)) ? Directory.GetParent(dir).FullName + "\\" : subDirectories[dir]; var archiveFileInfo = new ArchiveFileInfo() { FileName = info.Name, KeyName = f.Replace(baseToRemove, "").Replace('\\', '/'), FilePath = f, Size = info.Length, LastModified = info.LastWriteTimeUtc, MD5 = Utilities.GetMD5Hash(f), IsUploaded = false }; this._archiveInfo.Add(archiveFileInfo); size += archiveFileInfo.Size; } } ); } } // Remove the subdirectories from the directories list, so only the initially selected // directory(ies) is left. We need to keep clean the directories list, so the code below // suggesting an upload name gets the corrent amount of initially selected directories. foreach (string subDir in subDirectories.Values) { directories.Remove(subDir); } subDirectories.Clear(); subDirectories = null; // do the same for each individually selected files. foreach (var f in files) { if (this._archiveInfo.Select(x => x.FilePath).Contains(f)) { continue; } var fileCategory = Utilities.CheckFileApiRestrictions(f); if (fileCategory != Enumerations.FileCategory.Normal) { this.HasInvalidFiles = true; this._excludedFiles.Add(f, fileCategory); continue; } var info = new FileInfo(f); // Check that the archive size does not exceed the maximum allowed file size. // S3 supports multipart uploads with up to 10000 parts and 5 TB max size. // Since DF supports part size of 5 MB, archive size must not exceed 5 MB * 10000 if (info.Length > MAX_ALLOWED_FILE_SIZE) { throw new Exception("The file " + info + " exceeds the maximum allowed archive size of " + LongToSizeString.ConvertToString((double)MAX_ALLOWED_FILE_SIZE) + "."); } var archiveFileInfo = new ArchiveFileInfo() { FileName = info.Name, KeyName = info.Name, FilePath = f, Size = info.Length, LastModified = info.LastWriteTimeUtc, MD5 = Utilities.GetMD5Hash(f), IsUploaded = false }; this._archiveInfo.Add(archiveFileInfo); size += archiveFileInfo.Size; } var result = await ShowRestrictedWarningMessage(); // if the user clicked cancel in the warning message box because of a restricted folder // then cancel the new archive archive creation and reset the ViewModel. if (result == MessageBoxResult.Cancel) { this._isUserCancel = true; throw new Exception("User cancelled because of a restricted folder"); } if (this._archiveInfo.Count == 0) { throw new Exception("Your selection doesn't contain any files. Nothing to upload."); } // check that the archive size fits in user's DF storage. if (size > (this._deepfreezeClient.Settings.ActiveUser.Quota.Size - this._deepfreezeClient.Settings.ActiveUser.Quota.Used)) { // get the userviewmodel to refresh stats. // we could use the messaging service, but we actually need to wait until the stats are refreshed // before checking the sizes again. Sending a message is a fire and forget style, so that couldn't work here. var userVM = IoC.Get <IUserViewModel>() as UserViewModel; await userVM.RefreshUser(); if (size > (this._deepfreezeClient.Settings.ActiveUser.Quota.Size - this._deepfreezeClient.Settings.ActiveUser.Quota.Used)) { throw new Exception(Properties.Resources.ErrorNotEnoughSpaceGenericText); } } // suggest an archive title step 2 // if the title wasn't set in the previous step, suggest one. if (String.IsNullOrEmpty(this.ArchiveTitle)) { if (this._archiveInfo.Count == 1) { this.ArchiveTitle = this._archiveInfo.First().FileName; } else { this.ArchiveTitle = "upload-" + String.Format("{0:yyyy-MM-dd}", DateTime.Now); } } this._archiveSize = size; this.SetTotalsTexts(); this.HasChosenFiles = true; return; } catch (Exception e) { _log.Error(Utilities.GetCallerName() + " threw " + e.GetType().ToString() + " with message \"" + e.Message + "\"", e); if (!this._isUserCancel) { this.ErrorSelectingFiles = Properties.Resources.ErrorAddingFilesGenericText; } if (e is UnauthorizedAccessException || e is DirectoryNotFoundException || e is FileNotFoundException) { this.ErrorSelectingFiles += " " + e.Message; } else { if (e.Message == Properties.Resources.ErrorNotEnoughSpaceGenericText) { this.ErrorSelectingFiles = e.Message; } // if the selection includes no files and a restricted folder WASN'T in the selection // then show the no files to upload error message. if (this._archiveInfo.Count == 0 && !this._isUserCancel) { this.ErrorSelectingFiles = e.Message; } } this.IsReset = true; if (this._isUserCancel) { this.Reset(); } } finally { IsBusy = false; } }