Try your search with a different keyword or use * as a wildcard.
?using System.IO.Compression;
using System.Text;
using Microsoft.Extensions.FileProviders;
using Newtonsoft.Json;
using Nop.Core.Domain.Media;
using Nop.Core.Infrastructure;
using SkiaSharp;
namespace Nop.Services.Media.RoxyFileman;
///
/// Looks up and manages uploaded files using the on-disk file system
///
public partial class RoxyFilemanFileProvider : PhysicalFileProvider, IRoxyFilemanFileProvider
{
#region Fields
protected INopFileProvider _nopFileProvider;
protected readonly MediaSettings _mediaSettings;
#endregion
#region Ctor
public RoxyFilemanFileProvider(INopFileProvider nopFileProvider) : base(nopFileProvider.Combine(nopFileProvider.WebRootPath, NopRoxyFilemanDefaults.DefaultRootDirectory))
{
_nopFileProvider = nopFileProvider;
}
public RoxyFilemanFileProvider(INopFileProvider defaultFileProvider, MediaSettings mediaSettings) : this(defaultFileProvider)
{
_mediaSettings = mediaSettings;
}
#endregion
#region Utilities
///
/// Adjust image measures to target size
///
/// Source image
/// Target width
/// Target height
/// Adjusted width and height
protected virtual (int width, int height) ValidateImageMeasures(SKBitmap image, int maxWidth = 0, int maxHeight = 0)
{
ArgumentNullException.ThrowIfNull(image);
float width = Math.Min(image.Width, maxWidth);
float height = Math.Min(image.Height, maxHeight);
var targetSize = Math.Max(width, height);
if (image.Height > image.Width)
{
// portrait
width = image.Width * (targetSize / image.Height);
height = targetSize;
}
else
{
// landscape or square
width = targetSize;
height = image.Height * (targetSize / image.Width);
}
return ((int)width, (int)height);
}
///
/// Get a file type by the specified path string
///
/// The path string from which to get the file type
/// File type
protected virtual string GetFileType(string subpath)
{
var fileExtension = Path.GetExtension(subpath)?.ToLowerInvariant();
return fileExtension switch
{
".jpg" or ".jpeg" or ".png" or ".gif" or ".webp" or ".svg" => "image",
".swf" or ".flv" => "flash",
".mp4" or ".webm" or ".ogg" or ".mov" or ".m4a" or ".mp3" or ".wav" => "media",
_ => "file"
};
/* These media extensions are not supported by HTML5 or tinyMCE out of the box
* but may possibly be supported if You find players for them.
* if (fileExtension == ".3gp" || fileExtension == ".flv"
* || fileExtension == ".rmvb" || fileExtension == ".wmv" || fileExtension == ".divx"
* || fileExtension == ".divx" || fileExtension == ".mpg" || fileExtension == ".rmvb"
* || fileExtension == ".vob" // video
* || fileExtension == ".aif" || fileExtension == ".aiff" || fileExtension == ".amr"
* || fileExtension == ".asf" || fileExtension == ".asx" || fileExtension == ".wma"
* || fileExtension == ".mid" || fileExtension == ".mp2") // audio
* fileType = "media"; */
}
///
/// Get the absolute path for the specified path string in the root directory for this instance
///
/// The file or directory for which to obtain absolute path information
/// The fully qualified location of path, such as "C:\MyFile.txt"
protected virtual string GetFullPath(string path)
{
if (string.IsNullOrEmpty(path))
throw new RoxyFilemanException("NoFilesFound");
path = path.Trim(Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar);
if (Path.IsPathRooted(path))
throw new RoxyFilemanException("NoFilesFound");
var fullPath = Path.GetFullPath(Path.Combine(Root, path));
if (!IsUnderneathRoot(fullPath))
throw new RoxyFilemanException("NoFilesFound");
return fullPath;
}
///
/// Get image format by mime type
///
/// Mime type
/// SKEncodedImageFormat
protected virtual SKEncodedImageFormat GetImageFormatByMimeType(string mimeType)
{
var format = SKEncodedImageFormat.Jpeg;
if (string.IsNullOrEmpty(mimeType))
return format;
var parts = mimeType.ToLowerInvariant().Split('/');
var lastPart = parts[^1];
switch (lastPart)
{
case "webp":
format = SKEncodedImageFormat.Webp;
break;
case "png":
case "gif":
case "bmp":
case "x-icon":
format = SKEncodedImageFormat.Png;
break;
default:
break;
}
return format;
}
///
/// Get the unique name of the file (add -copy-(N) to the file name if there is already a file with that name in the directory)
///
/// Path to the file directory
/// Original file name
/// Unique name of the file
protected virtual string GetUniqueFileName(string directoryPath, string fileName)
{
var uniqueFileName = fileName;
var i = 0;
while (GetFileInfo(Path.Combine(directoryPath, uniqueFileName)) is IFileInfo fileInfo && fileInfo.Exists)
{
uniqueFileName = $"{Path.GetFileNameWithoutExtension(fileName)}-Copy-{++i}{Path.GetExtension(fileName)}";
}
return uniqueFileName;
}
///
/// Check the specified path is in the root directory of this instance
///
/// The absolute path
/// True if passed path is in the root; otherwise false
protected virtual bool IsUnderneathRoot(string fullPath)
{
return fullPath
.StartsWith(Root, StringComparison.OrdinalIgnoreCase);
}
///
/// Scale image to fit the destination sizes
///
/// Image data
/// SkiaSharp image format
/// Target width
/// Target height
/// The byte array of resized image
protected virtual byte[] ResizeImage(byte[] data, SKEncodedImageFormat format, int maxWidth, int maxHeight)
{
using var sourceStream = new SKMemoryStream(data);
using var inputData = SKData.Create(sourceStream);
using var image = SKBitmap.Decode(inputData);
var (width, height) = ValidateImageMeasures(image, maxWidth, maxHeight);
var toBitmap = new SKBitmap(width, height, image.ColorType, image.AlphaType);
if (!image.ScalePixels(toBitmap, SKFilterQuality.None))
throw new Exception("Image scaling");
var newImage = SKImage.FromBitmap(toBitmap);
var imageData = newImage.Encode(format, _mediaSettings.DefaultImageQuality);
newImage.Dispose();
return imageData.ToArray();
}
#endregion
#region Methods
///
/// Moves a file or a directory and its contents to a new location
///
/// The path of the file or directory to move
///
/// The path to the new location for sourceDirName. If sourceDirName is a file, then destDirName
/// must also be a file name
///
public virtual void DirectoryMove(string sourceDirName, string destDirName)
{
if (destDirName.StartsWith(sourceDirName, StringComparison.InvariantCulture))
throw new RoxyFilemanException("E_CannotMoveDirToChild");
var sourceDirInfo = new DirectoryInfo(GetFullPath(sourceDirName));
if (!sourceDirInfo.Exists)
throw new RoxyFilemanException("E_MoveDirInvalisPath");
if (string.Equals(sourceDirInfo.FullName, Root, StringComparison.InvariantCultureIgnoreCase))
throw new RoxyFilemanException("E_MoveDir");
var newFullPath = Path.Combine(GetFullPath(destDirName), sourceDirInfo.Name);
var destinationDirInfo = new DirectoryInfo(newFullPath);
if (destinationDirInfo.Exists)
throw new RoxyFilemanException("E_DirAlreadyExists");
try
{
sourceDirInfo.MoveTo(destinationDirInfo.FullName);
}
catch
{
throw new RoxyFilemanException("E_MoveDir");
}
}
///
/// Locate a file at the given path by directly mapping path segments to physical directories.
///
/// A path under the root directory
/// The file information. Caller must check Microsoft.Extensions.FileProviders.IFileInfo.Exists property.
public new IFileInfo GetFileInfo(string subpath)
{
if (string.IsNullOrEmpty(subpath))
return new NotFoundFileInfo(subpath);
subpath = subpath.TrimStart(Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar);
// Absolute paths not permitted.
if (Path.IsPathRooted(subpath))
return new NotFoundFileInfo(subpath);
return base.GetFileInfo(subpath);
}
///
/// Create configuration file for RoxyFileman
///
/// The base path for the store
/// Two-letter language code
/// A task that represents the asynchronous operation
public virtual async Task GetOrCreateConfigurationAsync(string pathBase, string lang)
{
//check whether the path base has changed, otherwise there is no need to overwrite the configuration file
if (Singleton.Instance?.RETURN_URL_PREFIX?.Equals(pathBase) ?? false)
{
return Singleton.Instance;
}
var filePath = _nopFileProvider.GetAbsolutePath(NopRoxyFilemanDefaults.ConfigurationFile);
//create file if not exists
_nopFileProvider.CreateFile(filePath);
//try to read existing configuration
var existingText = await _nopFileProvider.ReadAllTextAsync(filePath, Encoding.UTF8);
var existingConfiguration = JsonConvert.DeserializeObject(existingText);
//create configuration
var configuration = new RoxyFilemanConfig
{
FILES_ROOT = existingConfiguration?.FILES_ROOT ?? NopRoxyFilemanDefaults.DefaultRootDirectory,
SESSION_PATH_KEY = existingConfiguration?.SESSION_PATH_KEY ?? string.Empty,
THUMBS_VIEW_WIDTH = existingConfiguration?.THUMBS_VIEW_WIDTH ?? 140,
THUMBS_VIEW_HEIGHT = existingConfiguration?.THUMBS_VIEW_HEIGHT ?? 120,
PREVIEW_THUMB_WIDTH = existingConfiguration?.PREVIEW_THUMB_WIDTH ?? 300,
PREVIEW_THUMB_HEIGHT = existingConfiguration?.PREVIEW_THUMB_HEIGHT ?? 200,
MAX_IMAGE_WIDTH = existingConfiguration?.MAX_IMAGE_WIDTH ?? _mediaSettings.MaximumImageSize,
MAX_IMAGE_HEIGHT = existingConfiguration?.MAX_IMAGE_HEIGHT ?? _mediaSettings.MaximumImageSize,
DEFAULTVIEW = existingConfiguration?.DEFAULTVIEW ?? "list",
FORBIDDEN_UPLOADS = existingConfiguration?.FORBIDDEN_UPLOADS ?? string.Join(" ", NopRoxyFilemanDefaults.ForbiddenUploadExtensions),
ALLOWED_UPLOADS = existingConfiguration?.ALLOWED_UPLOADS ?? string.Empty,
FILEPERMISSIONS = existingConfiguration?.FILEPERMISSIONS ?? "0644",
DIRPERMISSIONS = existingConfiguration?.DIRPERMISSIONS ?? "0755",
LANG = existingConfiguration?.LANG ?? lang,
DATEFORMAT = existingConfiguration?.DATEFORMAT ?? "dd/MM/yyyy HH:mm",
OPEN_LAST_DIR = existingConfiguration?.OPEN_LAST_DIR ?? "yes",
//no need user to configure
INTEGRATION = "custom",
RETURN_URL_PREFIX = $"{pathBase}/images/uploaded/",
DIRLIST = $"{pathBase}/Admin/RoxyFileman/DirectoriesList",
CREATEDIR = $"{pathBase}/Admin/RoxyFileman/CreateDirectory",
DELETEDIR = $"{pathBase}/Admin/RoxyFileman/DeleteDirectory",
MOVEDIR = $"{pathBase}/Admin/RoxyFileman/MoveDirectory",
COPYDIR = $"{pathBase}/Admin/RoxyFileman/CopyDirectory",
RENAMEDIR = $"{pathBase}/Admin/RoxyFileman/RenameDirectory",
FILESLIST = $"{pathBase}/Admin/RoxyFileman/FilesList",
UPLOAD = $"{pathBase}/Admin/RoxyFileman/UploadFiles",
DOWNLOAD = $"{pathBase}/Admin/RoxyFileman/DownloadFile",
DOWNLOADDIR = $"{pathBase}/Admin/RoxyFileman/DownloadDirectory",
DELETEFILE = $"{pathBase}/Admin/RoxyFileman/DeleteFile",
MOVEFILE = $"{pathBase}/Admin/RoxyFileman/MoveFile",
COPYFILE = $"{pathBase}/Admin/RoxyFileman/CopyFile",
RENAMEFILE = $"{pathBase}/Admin/RoxyFileman/RenameFile",
GENERATETHUMB = $"{pathBase}/Admin/RoxyFileman/CreateImageThumbnail"
};
//save the file
var text = JsonConvert.SerializeObject(configuration, Formatting.Indented);
await File.WriteAllTextAsync(filePath, text, Encoding.UTF8);
Singleton.Instance = configuration;
return configuration;
}
///
/// Get all available directories as a directory tree
///
/// Type of the file
/// A value indicating whether to return a directory tree recursively
/// Path to start directory
public virtual IEnumerable GetDirectories(string type, bool isRecursive = true, string rootDirectoryPath = "")
{
foreach (var item in GetDirectoryContents(rootDirectoryPath))
{
if (item.IsDirectory)
{
var dirInfo = new DirectoryInfo(item.PhysicalPath);
yield return new RoxyDirectoryInfo(
getRelativePath(item.Name),
dirInfo.GetFiles().Count(x => isMatchType(x.Name)),
dirInfo.GetDirectories().Length);
}
if (!isRecursive)
break;
foreach (var subDir in GetDirectories(type, isRecursive, getRelativePath(item.Name)))
yield return subDir;
}
string getRelativePath(string name) => Path.Combine(rootDirectoryPath, name);
bool isMatchType(string name) => string.IsNullOrEmpty(type) || GetFileType(name) == type;
}
///
/// Get files in the passed directory
///
/// Path to the files directory
/// Type of the files
///
/// The list of
///
public virtual IEnumerable GetFiles(string directoryPath = "", string type = "")
{
var files = GetDirectoryContents(directoryPath);
return files
.Where(f => !f.IsDirectory && isMatchType(f.Name))
.Select(f =>
{
var width = 0;
var height = 0;
if (GetFileType(f.Name) == "image")
{
using var skData = SKData.Create(f.PhysicalPath);
if (skData != null)
{
var image = SKBitmap.DecodeBounds(skData);
width = image.Width;
height = image.Height;
}
}
return new RoxyFileInfo(getRelativePath(f.Name), f.LastModified, f.Length, width, height);
});
bool isMatchType(string name) => string.IsNullOrEmpty(type) || GetFileType(name) == type;
string getRelativePath(string name) => Path.Combine(directoryPath, name);
}
///
/// Moves a specified file to a new location, providing the option to specify a new file name
///
/// The name of the file to move. Can include a relative or absolute path
/// The new path and name for the file
public virtual void FileMove(string sourcePath, string destinationPath)
{
var sourceFile = GetFileInfo(sourcePath);
if (!sourceFile.Exists)
throw new RoxyFilemanException("E_MoveFileInvalisPath");
var destinationFile = GetFileInfo(destinationPath);
if (destinationFile.Exists)
throw new RoxyFilemanException("E_MoveFileAlreadyExists");
try
{
new FileInfo(sourceFile.PhysicalPath)
.MoveTo(GetFullPath(destinationPath));
}
catch
{
throw new RoxyFilemanException("E_MoveFile");
}
}
///
/// Copy the directory with the embedded files and directories
///
/// Path to the source directory
/// Path to the destination directory
public virtual void CopyDirectory(string sourcePath, string destinationPath)
{
var sourceDirInfo = new DirectoryInfo(GetFullPath(sourcePath));
if (!sourceDirInfo.Exists)
throw new RoxyFilemanException("E_CopyDirInvalidPath");
var newPath = Path.Combine(GetFullPath(destinationPath), sourceDirInfo.Name);
var destinationDirInfo = new DirectoryInfo(newPath);
if (destinationDirInfo.Exists)
throw new RoxyFilemanException("E_DirAlreadyExists");
try
{
destinationDirInfo.Create();
foreach (var file in sourceDirInfo.GetFiles())
{
var newFile = GetFileInfo(Path.Combine(destinationPath, file.Name));
if (!newFile.Exists)
file.CopyTo(Path.Combine(destinationDirInfo.FullName, file.Name));
}
foreach (var directory in sourceDirInfo.GetDirectories())
{
var destinationSubPath = Path.Combine(destinationPath, sourceDirInfo.Name, directory.Name);
var sourceSubPath = Path.Combine(sourcePath, directory.Name);
CopyDirectory(sourceSubPath, destinationSubPath);
}
}
catch
{
throw new RoxyFilemanException("E_CopyFile");
}
}
///
/// Rename the directory
///
/// Path to the source directory
/// New name of the directory
/// A task that represents the asynchronous operation
public virtual void RenameDirectory(string sourcePath, string newName)
{
try
{
var destinationPath = Path.Combine(Path.GetDirectoryName(sourcePath), newName);
DirectoryMove(sourcePath, destinationPath);
}
catch (Exception ex)
{
throw new RoxyFilemanException("E_RenameDir", ex);
}
}
///
/// Rename the file
///
/// Path to the source file
/// New name of the file
/// A task that represents the asynchronous operation
public virtual void RenameFile(string sourcePath, string newName)
{
try
{
var destinationPath = Path.Combine(Path.GetDirectoryName(sourcePath), newName);
FileMove(sourcePath, destinationPath);
}
catch (Exception ex)
{
throw new RoxyFilemanException("E_RenameFile", ex);
}
}
///
/// Delete the file
///
/// Path to the file
/// A task that represents the asynchronous operation
public virtual void DeleteFile(string path)
{
var fileToDelete = GetFileInfo(path);
if (!fileToDelete.Exists)
throw new RoxyFilemanException("E_DeleteFileInvalidPath");
try
{
File.Delete(GetFullPath(path));
}
catch
{
throw new RoxyFilemanException("E_DeleteFile");
}
}
///
/// Copy the file
///
/// Path to the source file
/// Path to the destination file
/// A task that represents the asynchronous operation
public virtual void CopyFile(string sourcePath, string destinationPath)
{
var sourceFile = GetFileInfo(sourcePath);
if (!sourceFile.Exists)
throw new RoxyFilemanException("E_CopyFileInvalidPath");
var newFilePath = Path.Combine(destinationPath, sourceFile.Name);
var destinationFile = GetFileInfo(newFilePath);
if (destinationFile.Exists)
newFilePath = Path.Combine(destinationPath, GetUniqueFileName(destinationPath, sourceFile.Name));
try
{
File.Copy(sourceFile.PhysicalPath, GetFullPath(newFilePath));
}
catch
{
throw new RoxyFilemanException("E_CopyFile");
}
}
///
/// Create the new directory
///
/// Path to the parent directory
/// Name of the new directory
/// A task that represents the asynchronous operation
public virtual void CreateDirectory(string parentDirectoryPath, string name)
{
//validate path and get absolute form
var fullPath = GetFullPath(Path.Combine(parentDirectoryPath, name));
var newDirectory = new DirectoryInfo(fullPath);
if (!newDirectory.Exists)
newDirectory.Create();
}
///
/// Delete the directory
///
/// Path to the directory
public virtual void DeleteDirectory(string path)
{
var sourceDirInfo = new DirectoryInfo(GetFullPath(path));
if (!sourceDirInfo.Exists)
throw new RoxyFilemanException("E_DeleteDirInvalidPath");
if (string.Equals(sourceDirInfo.FullName, Root, StringComparison.InvariantCultureIgnoreCase))
throw new RoxyFilemanException("E_CannotDeleteRoot");
if (sourceDirInfo.GetFiles().Length > 0 || sourceDirInfo.GetDirectories().Length > 0)
throw new RoxyFilemanException("E_DeleteNonEmpty");
try
{
sourceDirInfo.Delete();
}
catch
{
throw new RoxyFilemanException("E_CannotDeleteDir");
}
}
///
/// Save file in the root directory for this instance
///
/// Directory path in the root
/// The file name and extension
/// Mime type
/// The stream to read
/// A task that represents the asynchronous operation
public virtual async Task SaveFileAsync(string directoryPath, string fileName, string contentType, Stream fileStream)
{
var uniqueFileName = GetUniqueFileName(directoryPath, Path.GetFileName(fileName));
var destinationFile = Path.Combine(directoryPath, uniqueFileName);
await using var stream = new FileStream(GetFullPath(destinationFile), FileMode.Create);
if (GetFileType(Path.GetExtension(uniqueFileName)) == "image")
{
using var memoryStream = new MemoryStream();
await fileStream.CopyToAsync(memoryStream);
var roxyConfig = Singleton.Instance;
var imageData = ResizeImage(memoryStream.ToArray(),
GetImageFormatByMimeType(contentType),
roxyConfig?.MAX_IMAGE_WIDTH ?? _mediaSettings.MaximumImageSize,
roxyConfig?.MAX_IMAGE_HEIGHT ?? _mediaSettings.MaximumImageSize);
await stream.WriteAsync(imageData);
}
else
{
await fileStream.CopyToAsync(stream);
}
await stream.FlushAsync();
}
///
/// Get the thumbnail of the image
///
/// File path
/// Mime type
/// Byte array of the specified image
public virtual byte[] CreateImageThumbnail(string sourcePath, string contentType)
{
var imageInfo = GetFileInfo(sourcePath);
if (!imageInfo.Exists)
throw new RoxyFilemanException("Image not found");
var roxyConfig = Singleton.Instance;
using var imageStream = imageInfo.CreateReadStream();
using var ms = new MemoryStream();
imageStream.CopyTo(ms);
return ResizeImage(
ms.ToArray(),
GetImageFormatByMimeType(contentType),
roxyConfig.THUMBS_VIEW_WIDTH,
roxyConfig.THUMBS_VIEW_HEIGHT);
}
///
/// Create a zip archive of the specified directory.
///
/// The directory path with files to compress
/// The byte array
public virtual byte[] CreateZipArchiveFromDirectory(string directoryPath)
{
var sourceDirInfo = new DirectoryInfo(GetFullPath(directoryPath));
if (!sourceDirInfo.Exists)
throw new RoxyFilemanException("E_CreateArchive");
using var memoryStream = new MemoryStream();
using (var archive = new ZipArchive(memoryStream, ZipArchiveMode.Create, true))
{
foreach (var file in sourceDirInfo.EnumerateFiles("*", SearchOption.AllDirectories))
{
var fileRelativePath = file.FullName.Replace(sourceDirInfo.FullName, string.Empty)
.TrimStart('\\');
using var fileStream = file.OpenRead();
using var fileStreamInZip = archive.CreateEntry(fileRelativePath).Open();
fileStream.CopyTo(fileStreamInZip);
}
}
//ToArray() should be outside of the archive using
return memoryStream.ToArray();
}
#endregion
}