/// <summary> /// Calculates the size of the given directory. Displays a progress bar. /// </summary> /// <param name="directory">Directory, which size has to be calculated.</param> /// <param name="includeNested">If true, size will include all nested directories.</param> /// <param name="progressBarPrefix">Optional string added to the beginning of the progress bar window header.</param> /// <returns>Size of the directory in bytes.</returns> private static long GetDirectorySize(DirectoryInfo directory, bool includeNested = false, string progressBarPrefix = "") { EditorUtility.DisplayProgressBar($"{progressBarPrefix}Calculating size of directories...", $"Scanning '{directory.FullName}'...", 0f); // Calculate size of all files in directory. var enumeratedFiles = directory.EnumerateFiles(); long filesSize = enumeratedFiles.Sum((FileInfo file) => { // Fix which allows to exceed MAX_PATH when working with files in Windows. // Without it, files with full path longer than 259 characters will throw a FileNotFoundException. if (Application.platform == RuntimePlatform.WindowsEditor) { file = new FileInfo($"\\\\?\\{file.FullName}"); } return(file.Length); }); // Calculate size of all nested directories. long directoriesSize = 0; if (includeNested) { IEnumerable <DirectoryInfo> nestedDirectories = directory.EnumerateDirectories(); foreach (DirectoryInfo nestedDir in nestedDirectories) { directoriesSize += ProjectCloner.GetDirectorySize(nestedDir, true, progressBarPrefix); } } return(filesSize + directoriesSize); }
/// <summary> /// Copies directory located at sourcePath to destinationPath. Displays a progress bar. /// Same as the previous method, but uses recursion to copy all nested folders as well. /// </summary> /// <param name="source">Directory to be copied.</param> /// <param name="destination">Destination directory (created automatically if needed).</param> /// <param name="totalBytes">Total bytes to be copied. Calculated automatically, initialize at 0.</param> /// <param name="copiedBytes">To track already copied bytes. Calculated automatically, initialize at 0.</param> /// <param name="progressBarPrefix">Optional string added to the beginning of the progress bar window header.</param> private static void CopyDirectoryWithProgressBarRecursive(DirectoryInfo source, DirectoryInfo destination, ref long totalBytes, ref long copiedBytes, string progressBarPrefix = "") { /// Directory cannot be copied into itself. if (source.FullName.ToLower() == destination.FullName.ToLower()) { Debug.LogError("Cannot copy directory into itself."); return; } /// Calculate total bytes, if required. if (totalBytes == 0) { totalBytes = ProjectCloner.GetDirectorySize(source, true, progressBarPrefix); } /// Create destination directory, if required. if (!Directory.Exists(destination.FullName)) { Directory.CreateDirectory(destination.FullName); } /// Copy all files from the source. foreach (FileInfo file in source.GetFiles()) { try { file.CopyTo(Path.Combine(destination.ToString(), file.Name), true); } catch (IOException) { /// Some files may throw IOException if they are currently open in Unity editor. /// Just ignore them in such case. } /// Account the copied file size. copiedBytes += file.Length; /// Display the progress bar. float progress = (float)copiedBytes / (float)totalBytes; bool cancelCopy = EditorUtility.DisplayCancelableProgressBar( progressBarPrefix + "Copying '" + source.FullName + "' to '" + destination.FullName + "'...", "(" + (progress * 100f).ToString("F2") + "%) Copying file '" + file.Name + "'...", progress); if (cancelCopy) { return; } } /// Copy all nested directories from the source. foreach (DirectoryInfo sourceNestedDir in source.GetDirectories()) { DirectoryInfo nextDestingationNestedDir = destination.CreateSubdirectory(sourceNestedDir.Name); ProjectCloner.CopyDirectoryWithProgressBarRecursive(sourceNestedDir, nextDestingationNestedDir, ref totalBytes, ref copiedBytes, progressBarPrefix); } }
/// <summary> /// Calculates the size of the given directory. Displays a progress bar. /// </summary> /// <param name="directory">Directory, which size has to be calculated.</param> /// <param name="includeNested">If true, size will include all nested directories.</param> /// <param name="progressBarPrefix">Optional string added to the beginning of the progress bar window header.</param> /// <returns>Size of the directory in bytes.</returns> private static long GetDirectorySize(DirectoryInfo directory, bool includeNested = false, string progressBarPrefix = "") { EditorUtility.DisplayProgressBar(progressBarPrefix + "Calculating size of directories...", "Scanning '" + directory.FullName + "'...", 0f); /// Calculate size of all files in directory. long filesSize = directory.EnumerateFiles().Sum((FileInfo file) => file.Length); /// Calculate size of all nested directories. long directoriesSize = 0; if (includeNested) { IEnumerable <DirectoryInfo> nestedDirectories = directory.EnumerateDirectories(); foreach (DirectoryInfo nestedDir in nestedDirectories) { directoriesSize += ProjectCloner.GetDirectorySize(nestedDir, true, progressBarPrefix); } } return(filesSize + directoriesSize); }
/// <summary> /// Copies directory located at sourcePath to destinationPath. Displays a progress bar. /// Same as the previous method, but uses recursion to copy all nested folders as well. /// </summary> /// <param name="source">Directory to be copied.</param> /// <param name="destination">Destination directory (created automatically if needed).</param> /// <param name="totalBytes">Total bytes to be copied. Calculated automatically, initialize at 0.</param> /// <param name="copiedBytes">To track already copied bytes. Calculated automatically, initialize at 0.</param> /// <param name="progressBarPrefix">Optional string added to the beginning of the progress bar window header.</param> private static void CopyDirectoryWithProgressBarRecursive(DirectoryInfo source, DirectoryInfo destination, ref long totalBytes, ref long copiedBytes, string progressBarPrefix) { // Directory cannot be copied into itself. if (source.FullName.ToLower() == destination.FullName.ToLower()) { Debug.LogError("Cannot copy directory into itself."); return; } // Calculate total bytes, if required. if (totalBytes == 0) { totalBytes = ProjectCloner.GetDirectorySize(source, true, progressBarPrefix); } // Create destination directory, if required. if (!Directory.Exists(destination.FullName)) { Directory.CreateDirectory(destination.FullName); } // Copy all files from the source. foreach (FileInfo file in source.GetFiles()) { var currentFile = file; // Fix which allows to exceed MAX_PATH when working with files in Windows. // Without it, files with full path longer than 259 characters will throw a FileNotFoundException. if (Application.platform == RuntimePlatform.WindowsEditor) { currentFile = new FileInfo($"\\\\?\\{file.FullName}"); } try { currentFile.CopyTo(Path.Combine(destination.ToString(), currentFile.Name), true); } catch (IOException) { // Some files may throw IOException if they are currently open in Unity editor. // Just ignore them in such case. } // Account the copied file size. copiedBytes += currentFile.Length; // Display the progress bar. float progress = (float)copiedBytes / (float)totalBytes; bool cancelCopy = EditorUtility.DisplayCancelableProgressBar( $"{progressBarPrefix}Copying '{source.FullName}' to '{destination.FullName}'...", $"[{(progress * 100f):F2}%] Copying file '{currentFile.Name}'...", progress); if (cancelCopy) { return; } } // Copy all nested directories from the source. foreach (DirectoryInfo sourceNestedDir in source.GetDirectories()) { DirectoryInfo nextDestingationNestedDir = destination.CreateSubdirectory(sourceNestedDir.Name); ProjectCloner.CopyDirectoryWithProgressBarRecursive(sourceNestedDir, nextDestingationNestedDir, ref totalBytes, ref copiedBytes, progressBarPrefix); } }