protected internal virtual bool CheckUniqueFileName(MediaPathData pathData) { // (perf) First make fast check var exists = _fileRepo.Table.Any(x => x.Name == pathData.FileName && x.FolderId == pathData.Folder.Id); if (!exists) { return(false); } var q = new MediaSearchQuery { FolderId = pathData.Folder.Id, Term = string.Concat(pathData.FileTitle, "*.", pathData.Extension), Deleted = null }; var query = _searcher.PrepareQuery(q, MediaLoadFlags.AsNoTracking).Select(x => x.Name); var files = new HashSet <string>(query.ToList(), StringComparer.CurrentCultureIgnoreCase); if (_helper.CheckUniqueFileName(pathData.FileTitle, pathData.Extension, files, out var uniqueName)) { pathData.FileName = uniqueName; return(true); } return(false); }
public MediaPathData(MediaPathData pathData) { Node = pathData.Node; _name = pathData.FileName; _title = pathData._title; _ext = pathData._ext; _mime = pathData._mime; }
protected string GetCachedImagePath(int?mediaFileId, MediaPathData data, ProcessImageQuery query = null) { string result = ""; // xxxxxxx if (mediaFileId.GetValueOrDefault() > 0) { result = mediaFileId.Value.ToString(IdFormatString); } //// INFO: (mm) don't include folder id in pathes for now. It results in more complex image cache invalidation code. //// xxxxxxx-f //if (data.Folder != null) //{ // result = result.Grow(data.Folder.Id.ToString(CultureInfo.InvariantCulture), "-"); //} // xxxxxxx-f-abc result = result.Grow(data.FileTitle, "-"); if (result.IsEmpty()) { // files without name? No way! return(null); } if (query != null && query.NeedsProcessing()) { // xxxxxxx-f-abc-w100-h100 result += query.CreateHash(); } if (_mediaSettings.MultipleThumbDirectories && result.Length > MaxDirLength) { // Get the first four letters of the file name // 0001/xxxxxxx-f-abc-w100-h100 var subDirectoryName = result.Substring(0, MaxDirLength); result = subDirectoryName + "/" + result; } // 0001/xxxxxxx-f-abc-w100-h100.png return(result.Grow(data.Extension, ".")); }
public virtual CachedImage Get(int?mediaFileId, MediaPathData data, ProcessImageQuery query = null) { Guard.NotNull(data, nameof(data)); var resultExtension = query?.GetResultExtension(); if (resultExtension != null) { data.Extension = resultExtension; } var imagePath = GetCachedImagePath(mediaFileId, data, query); var file = _fileSystem.GetFile(BuildPath(imagePath)); var result = new CachedImage(file) { Path = imagePath, Extension = data.Extension, IsRemote = _fileSystem.IsCloudStorage }; return(result); }
public bool TokenizePath(string path, out MediaPathData data) { data = null; if (path.IsEmpty()) { return(false); } var dir = Path.GetDirectoryName(path); if (dir.HasValue()) { var node = _folderService.GetNodeByPath(dir); if (node != null) { data = new MediaPathData(node, path.Substring(dir.Length + 1)); return(true); } } return(false); }
private MediaPathData CreateDestinationPathData(MediaFile file, string destinationFileName) { if (!_helper.TokenizePath(destinationFileName, true, out var pathData)) { // Passed path is NOT a path, but a file name if (IsPath(destinationFileName)) { // ...but file name includes path chars, which is not allowed throw new ArgumentException( T("Admin.Media.Exception.InvalidPath", Path.GetDirectoryName(destinationFileName)), nameof(destinationFileName)); } if (file.FolderId == null) { throw new NotSupportedException(T("Admin.Media.Exception.FolderAssignment")); } pathData = new MediaPathData(_folderService.GetNodeById(file.FolderId.Value), destinationFileName); } return(pathData); }
private MediaFolderInfo InternalCopyFolder(TreeNode <MediaFolderNode> sourceNode, string destPath, DuplicateEntryHandling dupeEntryHandling, IList <DuplicateFileInfo> dupeFiles) { // Get dest node var destNode = _folderService.GetNodeByPath(destPath); // Dupe handling if (destNode != null && dupeEntryHandling == DuplicateEntryHandling.ThrowError) { throw _exceptionFactory.DuplicateFolder(sourceNode.Value.Path, destNode.Value); } var doDupeCheck = destNode != null; // Create dest folder if (destNode == null) { destNode = CreateFolder(destPath); } var ctx = _fileRepo.Context; // INFO: we gonna change file name during the files loop later. var destPathData = new MediaPathData(destNode, "placeholder.txt"); // Get all source files in one go var files = _searcher.SearchFiles( new MediaSearchQuery { FolderId = sourceNode.Value.Id }, MediaLoadFlags.AsNoTracking | MediaLoadFlags.WithTags).Load(); IDictionary <string, MediaFile> destFiles = null; HashSet <string> destNames = null; if (doDupeCheck) { // Get all files in destination folder for faster dupe selection destFiles = _searcher.SearchFiles(new MediaSearchQuery { FolderId = destNode.Value.Id }, MediaLoadFlags.None).ToDictionarySafe(x => x.Name); // Make a HashSet from all file names in the destination folder for faster unique file name lookups destNames = new HashSet <string>(destFiles.Keys, StringComparer.CurrentCultureIgnoreCase); } // Holds source and copy together, 'cause we perform a two-pass copy (file first, then data) var tuples = new List <(MediaFile, MediaFile)>(500); // Copy files batched foreach (var batch in files.Slice(500)) { foreach (var file in batch) { destPathData.FileName = file.Name; // >>> Do copy var copy = InternalCopyFile( file, destPathData, false /* copyData */, dupeEntryHandling, () => destFiles?.Get(file.Name), UniqueFileNameChecker, out var isDupe); if (copy != null) { if (isDupe) { dupeFiles.Add(new DuplicateFileInfo { SourceFile = ConvertMediaFile(file, sourceNode.Value), DestinationFile = ConvertMediaFile(copy, destNode.Value), UniquePath = destPathData.FullPath }); } if (!isDupe || dupeEntryHandling != DuplicateEntryHandling.Skip) { // When dupe: add to processing queue only if file was NOT skipped tuples.Add((file, copy)); } } } // Save batch to DB (1st pass) ctx.SaveChanges(); // Now copy file data foreach (var op in tuples) { InternalCopyFileData(op.Item1, op.Item2); } // Save batch to DB (2nd pass) ctx.SaveChanges(); ctx.DetachEntities <MediaFolder>(); ctx.DetachEntities <MediaFile>(); tuples.Clear(); } // Copy folders foreach (var node in sourceNode.Children) { destPath = destNode.Value.Path + "/" + node.Value.Name; InternalCopyFolder(node, destPath, dupeEntryHandling, dupeFiles); } return(new MediaFolderInfo(destNode)); void UniqueFileNameChecker(MediaPathData pathData) { if (destNames != null && _helper.CheckUniqueFileName(pathData.FileTitle, pathData.Extension, destNames, out var uniqueName)) { pathData.FileName = uniqueName; } } }
private bool ValidateMoveOperation( MediaFile file, string destinationFileName, DuplicateFileHandling dupeFileHandling, out bool nameChanged, out MediaPathData destPathData) { Guard.NotNull(file, nameof(file)); Guard.NotEmpty(destinationFileName, nameof(destinationFileName)); destPathData = CreateDestinationPathData(file, destinationFileName); var destFileName = destPathData.FileName; var destFolderId = destPathData.Folder.Id; var folderChanged = destFolderId != file.FolderId; var shouldRestore = false; nameChanged = !destFileName.IsCaseInsensitiveEqual(file.Name); if (file.FolderId.HasValue && folderChanged) { // When "Move" operation: ensure file stays in source album. ValidateAlbums("Move", file.FolderId.Value, destFolderId); } if (nameChanged) { // Ensure both MIME types are equal ValidateMimeTypes("Move", file.MimeType, destPathData.MimeType); } // Check whether destination file exists if (!folderChanged && file.Deleted) { // Special case where a file is moved from trash to its origin location. // In this case the file should just be restored without any dupe check. shouldRestore = true; } else { var dupe = _fileRepo.Table.FirstOrDefault(x => x.Name == destFileName && x.FolderId == destFolderId); if (dupe != null) { if (!folderChanged) { throw _exceptionFactory.IdenticalPaths(ConvertMediaFile(file, destPathData.Folder)); } switch (dupeFileHandling) { case DuplicateFileHandling.ThrowError: var fullPath = destPathData.FullPath; _helper.CheckUniqueFileName(destPathData.FileTitle, destPathData.Extension, dupe.Name, out _); throw _exceptionFactory.DuplicateFile(fullPath, ConvertMediaFile(dupe, destPathData.Folder), destPathData.FullPath); case DuplicateFileHandling.Rename: if (_helper.CheckUniqueFileName(destPathData.FileTitle, destPathData.Extension, dupe.Name, out var uniqueName)) { nameChanged = true; destPathData.FileName = uniqueName; return(true); } break; case DuplicateFileHandling.Overwrite: DeleteFile(dupe, true); break; } } } return(folderChanged || nameChanged || shouldRestore); }
private MediaFile InternalCopyFile( MediaFile file, MediaPathData destPathData, bool copyData, DuplicateEntryHandling dupeEntryHandling, Func <MediaFile> dupeFileSelector, Action <MediaPathData> uniqueFileNameChecker, out bool isDupe) { // Find dupe and handle isDupe = false; var dupe = dupeFileSelector(); if (dupe != null) { switch (dupeEntryHandling) { case DuplicateEntryHandling.Skip: isDupe = true; uniqueFileNameChecker(destPathData); return(dupe); case DuplicateEntryHandling.ThrowError: var fullPath = destPathData.FullPath; uniqueFileNameChecker(destPathData); throw _exceptionFactory.DuplicateFile(fullPath, ConvertMediaFile(dupe), destPathData.FullPath); case DuplicateEntryHandling.Rename: uniqueFileNameChecker(destPathData); dupe = null; break; case DuplicateEntryHandling.Overwrite: if (file.FolderId == destPathData.Folder.Id) { throw new IOException(T("Admin.Media.Exception.Overwrite")); } break; } } isDupe = dupe != null; var copy = dupe ?? new MediaFile(); // Simple clone MapMediaFile(file, copy); // Set folder id copy.FolderId = destPathData.Folder.Id; // A copied file cannot stay in deleted state copy.Deleted = false; // Set name stuff if (!copy.Name.IsCaseInsensitiveEqual(destPathData.FileName)) { copy.Name = destPathData.FileName; copy.Extension = destPathData.Extension; copy.MimeType = destPathData.MimeType; } // Save to DB if (isDupe) { _fileRepo.Update(copy); } else { _fileRepo.Insert(copy); } // Copy data: blob, alt, title etc. if (copyData) { InternalCopyFileData(file, copy); } return(copy); }
protected MediaStorageItem ProcessFile( ref MediaFile file, MediaPathData pathData, Stream inStream, bool isTransient = true, DuplicateFileHandling dupeFileHandling = DuplicateFileHandling.ThrowError, MimeValidationType mediaValidationType = MimeValidationType.MimeTypeMustMatch) { if (file != null) { if (dupeFileHandling == DuplicateFileHandling.ThrowError) { var fullPath = pathData.FullPath; CheckUniqueFileName(pathData); throw _exceptionFactory.DuplicateFile(fullPath, ConvertMediaFile(file, pathData.Folder), pathData.FullPath); } else if (dupeFileHandling == DuplicateFileHandling.Rename) { if (CheckUniqueFileName(pathData)) { file = null; } } } if (file != null && mediaValidationType != MimeValidationType.NoValidation) { if (mediaValidationType == MimeValidationType.MimeTypeMustMatch) { ValidateMimeTypes("Save", file.MimeType, pathData.MimeType); } else if (mediaValidationType == MimeValidationType.MediaTypeMustMatch) { ValidateMediaTypes("Save", _typeResolver.Resolve(pathData.Extension), file.MediaType); } // Restore file if soft-deleted file.Deleted = false; // Delete thumbnail _imageCache.Delete(file); } file = file ?? new MediaFile { IsTransient = isTransient, FolderId = pathData.Node.Value.Id }; // Untrackable folders can never contain transient files. if (!pathData.Folder.CanDetectTracks) { file.IsTransient = false; } var name = pathData.FileName; if (name != pathData.FileName) { pathData.FileName = name; } file.Name = pathData.FileName; file.Extension = pathData.Extension; file.MimeType = pathData.MimeType; if (file.MediaType == null) { file.MediaType = _typeResolver.Resolve(pathData.Extension, pathData.MimeType); } // Process image if (inStream != null && inStream.Length > 0 && file.MediaType == MediaType.Image && ProcessImage(file, inStream, out var outImage)) { file.Width = outImage.Size.Width; file.Height = outImage.Size.Height; file.PixelSize = outImage.Size.Width * outImage.Size.Height; return(MediaStorageItem.FromImage(outImage)); } else { file.RefreshMetadata(inStream); return(MediaStorageItem.FromStream(inStream)); } }