How to check exist file in global asax or bundles? - c#

I use Bundle. But it not send exception if file not found.
I need check exist file and catch exception if file not exist.
I tried:
var cssCommon = "/Common/common.css";
if (!System.IO.File.Exists(server.MapPath("~") + cssCommon))
{
throw new FileNotFoundException(cssCommon);
}
but always had exception
How check exist file on global asax or in bundle settings?

I prefer to use a BundleHelper for this task.
Herman has an excellent one here: https://stackoverflow.com/a/25784663/732377
Copied here for completeness, but all the kudos should go to Herman!
public static class BundleHelper
{
[Conditional("DEBUG")] // remove this attribute to validate bundles in production too
private static void CheckExistence(string virtualPath)
{
int i = virtualPath.LastIndexOf('/');
string path = HostingEnvironment.MapPath(virtualPath.Substring(0, i));
string fileName = virtualPath.Substring(i + 1);
bool found = Directory.Exists(path);
if (found)
{
if (fileName.Contains("{version}"))
{
var re = new Regex(fileName.Replace(".", #"\.").Replace("{version}", #"(\d+(?:\.\d+){1,3})"));
fileName = fileName.Replace("{version}", "*");
found = Directory.EnumerateFiles(path, fileName).Where(file => re.IsMatch(file)).FirstOrDefault() != null;
}
else // fileName may contain '*'
found = Directory.EnumerateFiles(path, fileName).FirstOrDefault() != null;
}
if (!found)
throw new ApplicationException(String.Format("Bundle resource '{0}' not found", virtualPath));
}
public static Bundle IncludeExisting(this Bundle bundle, params string[] virtualPaths)
{
foreach (string virtualPath in virtualPaths)
CheckExistence(virtualPath);
return bundle.Include(virtualPaths);
}
public static Bundle IncludeExisting(this Bundle bundle, string virtualPath, params IItemTransform[] transforms)
{
CheckExistence(virtualPath);
return bundle.Include(virtualPath, transforms);
}
}
Then in configuration:
bundles.Add(new ScriptBundle("~/test")
.IncludeExisting("~/Scripts/jquery/jquery-{version}.js")
.IncludeExisting("~/Scripts/lib*")
.IncludeExisting("~/Scripts/model.js")
);
However, you might also want to checkout other solutions to this common problem.
nrodic is pretty straight forward here: https://stackoverflow.com/a/24812225/732377

Related

Searching through all subfolders when using Resources.Load in Unity

Is it possible to have Resources.Load(name, type) search for a fitting asset not just in the base Resources folder / a specified subfolder, but instead the full subfolder structure under Resources?
Example folder structure of
Resources
- Subfolder
- image.png
I would like something like Resources.Load("image", typeof(Texture2D)) to return the image without the user having to specify "Subfolder/image".
I know it's ugly, but it's supposed to be a "drop it in your bashed together project without worrying about your folder structure"-type utility script and I won't know the subfolder.
The accepted answer only works in the editor context, and doesn't work after building the game. The resources folder is packed when building your game, and will no longer be a file hierarchy you can loop through with Directory.GetDirectories. The only way to get this to work is to save all file paths while still in the editor context, and use this file hierarchy to load the assets at runtime. In my project I used the following code to add a button to the component that uses the assets in the resources folder, named CharacterGen. When this button is clicked, all png files in all subfolders in the Resources folder are saved to the public attribute named filePaths that CharacterGen has.
[CustomEditor(typeof(CharacterGen))]
public class RefreshCharacterList : Editor
{
CharacterGen charGen;
public void OnEnable()
{
charGen = (CharacterGen)target;
}
public override void OnInspectorGUI()
{
base.OnInspectorGUI();
if (GUILayout.Button("Load resource paths"))
{
List<String> paths = new List<string>();
LoadPathsRecursive("", ref paths);
charGen.filePaths = paths;
EditorUtility.SetDirty(charGen); //original post didn't have this line, but this is needed to make sure your changes are saved.
}
}
void LoadPathsRecursive(string path, ref List<string> paths)
{
var fullPath = Application.dataPath + "/Resources/" + path;
Debug.Log("fullPath: " + fullPath);
DirectoryInfo dirInfo = new DirectoryInfo(fullPath);
foreach(var file in dirInfo.GetFiles())
{
if (file.Name.Contains(".PNG") && !file.Name.Contains(".meta"))
{
paths.Add(path + "/" + file.Name.Replace(".PNG", ""));
}
}
foreach (var dir in dirInfo.GetDirectories())
{
LoadPathsRecursive(path + "/" + dir.Name, ref paths);
}
}
}
In CharacterGen I later evoke Resources.Load, using the paths that were saved when clicking the button.
foreach (var filePath in filePaths)
{
var loadedSprite = Resources.Load<Sprite>(filePath);
//Do something with loadedSprite
}
Edit: I failed to mention an important detail in my original post. the filePaths is a Monobehaviour field, and I marked it with [SerializeField] (alternatively it could be marked as public) so that unity actually serializes the values of this field and includes it in the build.
There is no way to change the Resouces.Load() static method functionality, it's Unity Internal. However, you can write your own custom class that does your desired functionality. The code needs to find all the directories inside the Resources folder and search for the file. Let's call the class ResourcesExtension
public class ResourcesExtension
{
public static string ResourcesPath = Application.dataPath+"/Resources";
public static UnityEngine.Object Load(string resourceName, System.Type systemTypeInstance)
{
string[] directories = Directory.GetDirectories(ResourcesPath,"*",SearchOption.AllDirectories);
foreach (var item in directories)
{
string itemPath = item.Substring(ResourcesPath.Length+1);
UnityEngine.Object result = Resources.Load(itemPath+"\\"+resourceName,systemTypeInstance);
if(result!=null)
return result;
}
return null;
}
}
Then all you need to do is calling the static method.
ResourcesExtension.Load("image", typeof(Texture2D))
if you want to find all Object of a type and return as a List.
it Emad code Edited a bit.
private List<T> FindAllObject<T>()
{
List<T> tmp = new List<T>();
string ResourcesPath = Application.dataPath + "/Resources";
string[] directories = Directory.GetDirectories(ResourcesPath, "*", SearchOption.AllDirectories);
foreach (string item in directories)
{
string itemPath = item.Substring(ResourcesPath.Length + 1);
T[] reasult = Resources.LoadAll(itemPath, typeof(T)).Cast<T>().ToArray();
foreach (T x in reasult)
{
if (!tmp.Contains(x))
{
tmp.Add(x);
}
}
}
return tmp;
}
To use it.
List<MyClass> myClasses = FindAllObject<MyClass>();

HttpContext.Current.Request.Url.IsFile Returns False When Url is ASPX Page

I'm hitting a local URL of the form: http://localhost/example.dev/eu/default.aspx.
My goal is to determine when the request is an aspx file inside of the global.asax file, and subsequently do stuff if it is an aspx file (and only an aspx file) using:
HttpContext.Current.Request.Url.IsFile
It's consistently resolving to false however and I'm not sure why. My complete global.asax code is:
if(HttpContext.Current.Request.Url.IsFile)
{
if(File.Exists(HttpContext.Current.Request.Url.LocalPath))
{
if(new FileInfo(HttpContext.Current.Request.Url.LocalPath).Extension.Equals("aspx"))
{
DoSomethingWithThePagesURL();
}
}
}
Did you take a look at the Documentation for IsFile Property?. It seems very clear from the documentation that Http: is not File:.
The IsFile property is true when the Scheme property equals UriSchemeFile.
DotNetFiddle Example
using System;
public class Program
{
public static void Main()
{
Uri uriAddress2 = new Uri("file://server/filename.ext");
Console.WriteLine(uriAddress2.LocalPath);
Console.WriteLine("Uri {0} a UNC path", uriAddress2.IsUnc ? "is" : "is not");
Console.WriteLine("Uri {0} a local host", uriAddress2.IsLoopback ? "is" : "is not");
Console.WriteLine("Uri {0} a file", uriAddress2.IsFile ? "is" : "is not");
}
}
Results:
\server\filename.ext
Uri is a UNC path
Uri is not a local host
Uri is a file
I use Nuget package walter.web.firewall that inject a IPageRequest in each request, this contains access to the requests underlying resource and will provide access via IPageRequest.LocalFile
However if you do need a firewall and it has been a while since this was asked, and lot's of framework changes have taken place since the question was asked so let my try and answer it in a way that uses no framework classes hoping it will work for all that try and implement it in the future.
here's the code:
public enum FileLocation
{
NotSet,
Disk,
Resource,
}
private static readonly string[] FileExtenstions = new[] {
".js"
,".ts"
,".vue"
,".css"
,".jpg"
,".png"
,".gif"
,".ico"
,".svg"
,".ttf"
,".eot"
,".ttf"
,".woff"
,".woff2"
,".mp4"
,".mp3"
,".emf"
};
public FileLocation IsMappedTo(Uri uri)
{
if (uri is null)
{
throw new ArgumentNullException(nameof(uri));
}
//make sure we support .net default URI contract
if (uri.IsFile)
return FileLocation.Disk;
//now assume you are looking in a web application
var path = uri.AbsolutePath;
if (path.Length == 0 || path.Equals("/",StringComparison.Ordinal) || path.Length< FileExtenstions.Min(s=>s.Length))
return FileLocation.NotSet;
//get the directory normally one would use IWebHostEnvironment.ContentRootPath different versions .net will have other methods
var dir = Path.Combine(Directory.GetCurrentDirectory(), "wwwroot");
//get all resources names from the assembly hosting this class out side if the loop from this assembly you can also use
//you can also use GetManifestResourceNames() to use the web application's assembly
var resourceNames = new HashSet<string>(this.GetType().Assembly.GetManifestResourceNames());
var entryAssembly = Assembly.GetEntryAssembly();
if (entryAssembly != null && entryAssembly != this.GetType().Assembly)
{
foreach (var entry in entryAssembly.GetManifestResourceNames())
{
if (string.IsNullOrEmpty(entry))
resourceNames.Add(entry);
}
}
for (var i = 0; i < FileExtenstions.Length; i++)
{
if (FileExtenstions[i].Equals(path[FileExtenstions[i].Length..], StringComparison.OrdinalIgnoreCase) || path.Contains(FileExtenstions[i], StringComparison.OrdinalIgnoreCase))
{
//exists on disk
if (File.Exists(Path.Combine(dir, path.Replace("/", #"\"))))
return FileLocation.Disk;
//has a file as an embedded resource with the same name (ignores the path) so you might have duplicates names
if (resourceNames.Any(a => a.EndsWith(path.Split('/')[^1], StringComparison.OrdinalIgnoreCase)))
return FileLocation.Resource;
}
}
return FileLocation.NotSet;
}

string.IsNullOrEmpty to combine another directory

I want ask to find directory name it (folder1) to combine.
But if directory not exist, i want to find another directory name it (folder2) to combine.
What should i put to it?
Here the code:
public static string DataDirectory
{
get
{
if (string.IsNullOrEmpty(Directory))
return null;
return Path.Combine(Directory, "Data/folder1");
}
}
Thanks.
Directory.Exists should work fine
public static string DataDirectory
{
get
{
if (string.IsNullOrEmpty(Directory))
return null;
// Use Path.Combine just one time
string firstFolder = Path.Combine(Directory, "Data/folder1");
if(Directory.Exists(firstFolder)
return Path.Combine(firstFolder);
else
return Path.Combine(Directory, "Data/folder2");
}
}
you could also do something like this to first check if there are any directories, then use linq to order the directories and select the first element.
public static string GetDataDirectory(string root)
{
var directoryList = Directory.GetDirectories(root);
if (!directoryList.Any())
return null;
directoryList = directoryList.OrderBy(dir => dir).ToArray();
return directoryList.First();
}

SSH.NET SFTP Get a list of directories and files recursively

I am using Renci.SshNet library to get a list of files and directories recursively by using SFTP. I can able to connect SFTP site but I am not sure how to get a list of directories and files recursively in C#. I haven't found any useful examples.
Has anybody tried this? If so, can you post some sample code about how to get these files and folders recursively.
Thanks,
Prav
This library has some quirks that make this recursive listing tricky because the interaction between the ChangeDirectory and ListDirectory do not work as you may expect.
The following does not list the files in the /home directory instead it lists the files in the / (root) directory:
sftp.ChangeDirectory("home");
sftp.ListDirectory("").Select (s => s.FullName);
The following does not work and returns a SftpPathNotFoundException:
sftp.ChangeDirectory("home");
sftp.ListDirectory("home").Select (s => s.FullName);
The following is the correct way to list the files in the /home directory
sftp.ChangeDirectory("/");
sftp.ListDirectory("home").Select (s => s.FullName);
This is pretty crazy if you ask me. Setting the default directory with the ChangeDirectory method has no effect on the ListDirectory method unless you specify a folder in the parameter of this method. Seems like a bug should be written for this.
So when you write your recursive function you'll have to set the default directory once and then change the directory in the ListDirectory call as you iterate over the folders. The listing returns an enumerable of SftpFiles. These can then be checked individually for IsDirectory == true. Just be aware that the listing also returns the . and .. entries (which are directories). You'll want to skip these if you want to avoid an infinite loop. :-)
EDIT 2/23/2018
I was reviewing some of my old answers and would like to apologize for the answer above and supply the following working code. Note that this example does not require ChangeDirectory, since it's using the Fullname for the ListDirectory:
void Main()
{
using (var client = new Renci.SshNet.SftpClient("sftp.host.com", "user", "password"))
{
var files = new List<String>();
client.Connect();
ListDirectory(client, ".", ref files);
client.Disconnect();
files.Dump();
}
}
void ListDirectory(SftpClient client, String dirName, ref List<String> files)
{
foreach (var entry in client.ListDirectory(dirName))
{
if (entry.IsDirectory)
{
ListDirectory(client, entry.FullName, ref files);
}
else
{
files.Add(entry.FullName);
}
}
}
Try this:
var filePaths = client.ListDirectory(client.WorkingDirectory);
Here is a full class. It's .NET Core 2.1 Http trigger function app (v2)
I wanted to get rid of any directories that start with '.', cause my sftp server has .cache folders and .ssh folders with keys. Also didn't want to have to deal with folder names like '.' or '..'
What I will end up doing is projecting the SftpFile into a type that I work with and return that to the caller (in this case it will be a logic app). I'll then pass that object into a stored procedure and use OPENJSON to build up my monitoring table. This is basically the first step in creating my SFTP processing queue that will move files off my SFTP folder and into my Data Lake (blob for now until I come up with something better I guess).
The reason I used .WorkingDirectory is because I created a user with home directory as '/home'. This lets me traverse all of my user folders. My app doesn't need to have a specific folder as a starting point, just the user 'root' so to speak.
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Azure.WebJobs;
using Microsoft.Azure.WebJobs.Extensions.Http;
using Microsoft.Extensions.Logging;
using Renci.SshNet;
using Renci.SshNet.Sftp;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
namespace SFTPFileMonitor
{
public class GetListOfFiles
{
[FunctionName("GetListOfFiles")]
public async Task<IActionResult> RunAsync([HttpTrigger(AuthorizationLevel.Anonymous, "get")] HttpRequest req, ILogger log)
{
log.LogInformation("C# HTTP trigger function processed a request.");
List<SftpFile> zFiles;
int fileCount;
decimal totalSizeGB;
long totalSizeBytes;
using (SftpClient sftpClient = new SftpClient("hostname", "username", "password"))
{
sftpClient.Connect();
zFiles = await GetFiles(sftpClient, sftpClient.WorkingDirectory, new List<SftpFile>());
fileCount = zFiles.Count;
totalSizeBytes = zFiles.Sum(l => l.Length);
totalSizeGB = BytesToGB(totalSizeBytes);
}
return new OkObjectResult(new { fileCount, totalSizeBytes, totalSizeGB, zFiles });
}
private async Task<List<SftpFile>> GetFiles(SftpClient sftpClient, string directory, List<SftpFile> files)
{
foreach (SftpFile sftpFile in sftpClient.ListDirectory(directory))
{
if (sftpFile.Name.StartsWith('.')) { continue; }
if (sftpFile.IsDirectory)
{
await GetFiles(sftpClient, sftpFile.FullName, files);
}
else
{
files.Add(sftpFile);
}
}
return files;
}
private decimal BytesToGB(long bytes)
{
return Convert.ToDecimal(bytes) / 1024 / 1024 / 1024;
}
}
}
I have achieved this using recursion. Created a class TransportResponse like this
public class TransportResponse
{
public string directoryName { get; set; }
public string fileName { get; set; }
public DateTime fileTimeStamp { get; set; }
public MemoryStream fileStream { get; set; }
public List<TransportResponse> lstTransportResponse { get; set; }
}
I create a list of TransportResponse class. If the directoryName is not null, it will contain a list of the same class which will have the the files inside that directory as a MemoryStream ( this can be changed as per your use case)
List<TransportResponse> lstResponse = new List<TransportResponse>();
using (var client = new SftpClient(connectionInfo))
{
try
{
Console.WriteLine("Connecting to " + connectionInfo.Host + " ...");
client.Connect();
Console.WriteLine("Connected to " + connectionInfo.Host + " ...");
}
catch (Exception ex)
{
Console.WriteLine("Could not connect to "+ connectionInfo.Host +" server. Exception Details: " + ex.Message);
}
if (client.IsConnected)
{
var files = client.ListDirectory(transport.SourceFolder);
lstResponse = downloadFilesInDirectory(files, client);
client.Disconnect();
}
else
{
Console.WriteLine("Could not download files from "+ transport.TransportIdentifier +" because client was not connected.");
}
}
private static List<TransportResponse> downloadFilesInDirectory(IEnumerable<SftpFile> files, SftpClient client)
{
List<TransportResponse> lstResponse = new List<TransportResponse>();
foreach (var file in files)
{
if (!file.IsDirectory)
{
if (file.Name != "." && file.Name != "..")
{
if (!TransportDAL.checkFileExists(file.Name, file.LastWriteTime))
{
using (MemoryStream fs = new MemoryStream())
{
try
{
Console.WriteLine("Reading " + file.Name + "...");
client.DownloadFile(file.FullName, fs);
fs.Seek(0, SeekOrigin.Begin);
lstResponse.Add(new TransportResponse { fileName = file.Name, fileTimeStamp = file.LastWriteTime, fileStream = new MemoryStream(fs.GetBuffer()) });
}
catch(Exception ex)
{
Console.WriteLine("Error reading File. Exception Details: " + ex.Message);
}
}
}
else
{
Console.WriteLine("File was downloaded previously");
}
}
}
else
{
if (file.Name != "." && file.Name != "..")
{
lstResponse.Add(new TransportResponse { directoryName = file.Name,lstTransportResponse = downloadFilesInDirectory(client.ListDirectory(file.Name), client) });
}
}
}
return lstResponse;
}
Hope this helps. Thanks
#Carlos Bos
This library has some quirks that make this recursive listing tricky
because the interaction between the ChangeDirectory and ListDirectory
do not work as you may expect.
correct
It works well when the ChangeDirectory() parameter is "."
but if you do
SftpClient sftp ...;
sftp.ChangeDirectory("some_folder");
//get file list
List<SftpFile> fileList = sftp.ListDirectory("some_folder").ToList();
then there is an assertion because the ListDirectory() call expects
"some_folder/some_folder"
The workaround I use is to save and restore the current directory before a remote upload/rename to "some_folder", and you need to list that folder before the operation (e.g to see the file already exists)
string working_directory = sftp.WorkingDirectory;
sftp.ChangeDirectory("some_folder");
sftp.RenameFile("name", "new_name");
sftp.ChangeDirectory(working_directory);
to check if the file exists, this call is sufficient
sftp.Exists(path)
or if you want to add some other criteria, like case sensitive or not
public FileExistence checkFileExists(string folder, string fileName)
{
//get file list
List<SftpFile> fileList = sftp.ListDirectory(folder).ToList();
if (fileList == null)
{
return FileExistence.UNCONFIRMED;
}
foreach (SftpFile f in fileList)
{
Console.WriteLine(f.ToString());
//a not case sensitive comparison is made
if (f.IsRegularFile && f.Name.ToLower() == fileName.ToLower())
{
return FileExistence.EXISTS;
}
}
//if not found in traversal , it does not exist
return FileExistence.DOES_NOT_EXIST;
}
where FileExistence is
public enum FileExistence
{
EXISTS,
DOES_NOT_EXIST,
UNCONFIRMED
};
Extension Method:
public static IEnumerable<SftpFile> ListDirectoryRecursive(this SftpClient client, String dirName)
{
foreach (var entry in client.ListDirectory(dirName))
{
if (Regex.IsMatch(entry.Name, "^\\.+$")) continue;
yield return entry;
if (entry.IsDirectory)
foreach (var innerEntry in ListDirectoryRecursive(client, entry.FullName))
yield return innerEntry;
}
}

Converting a URI path to a relative file system path in .NET

How do I convert an absolute or relative URI path (e.g. /foo/bar.txt) to a (segmentwise) corresponding relative file system path (e.g. foo\bar.txt) in .NET?
My program is not an ASP.NET application.
Have you already tried Server.MapPath?
or Uri.LocalPath property? Something like following :
string uriString = "file://server/filename.ext";
// Lesson learnt - always check for a valid URI
if(Uri.IsWellFormedUriString(uriString))
{
Uri uri = new Uri(uriString);
Console.WriteLine(uri.LocalPath);
}
I figured out this way to produce a full absolute file system path from a relative or absolute URI and a base path.
With:
Uri basePathUri = new Uri(#"C:\abc\");
From a relative URI:
string filePath = new Uri(basePathUri, relativeUri).AbsolutePath;
From an absolute URI:
// baseUri is a URI used to derive a relative URI
Uri relativeUri = baseUri.MakeRelativeUri(absoluteUri);
string filePath = new Uri(basePathUri, relativeUri).AbsolutePath;
You can do this:
var localPath = Server.MapPath("/foo/bar.txt");
See MSDN for details
Not all have access to server.MapPath due to backend or framework changes, and there are lot's of way one of them is could be like this
public enum FileLocation
{
NotSet,
Disk,
Resource,
}
private static readonly string[] FileExtenstions = new[] {
".js"
,".ts"
,".vue"
,".css"
,".jpg"
,".png"
,".gif"
,".ico"
,".svg"
,".ttf"
,".eot"
,".ttf"
,".woff"
,".woff2"
,".mp4"
,".mp3"
,".emf"
};
public FileLocation IsMappedTo(Uri uri)
{
if (uri is null)
{
throw new ArgumentNullException(nameof(uri));
}
//make sure we support .net default URI contract
if (uri.IsFile)
return FileLocation.Disk;
//now assume you are looking in a web application
var path = uri.AbsolutePath;
if (path.Length == 0 || path.Equals("/",StringComparison.Ordinal) || path.Length< FileExtenstions.Min(s=>s.Length))
return FileLocation.NotSet;
//get the directory normally one would use IWebHostEnvironment.ContentRootPath different versions .net will have other methods
var dir = Path.Combine(Directory.GetCurrentDirectory(), "wwwroot");
//get all resources names from the assembly hosting this class out side if the loop from this assembly you can also use
//you can also use GetManifestResourceNames() to use the web application's assembly
var resourceNames = new HashSet<string>(this.GetType().Assembly.GetManifestResourceNames());
var entryAssembly = Assembly.GetEntryAssembly();
if (entryAssembly != null && entryAssembly != this.GetType().Assembly)
{
foreach (var entry in entryAssembly.GetManifestResourceNames())
{
if (string.IsNullOrEmpty(entry))
resourceNames.Add(entry);
}
}
for (var i = 0; i < FileExtenstions.Length; i++)
{
if (FileExtenstions[i].Equals(path[FileExtenstions[i].Length..], StringComparison.OrdinalIgnoreCase) || path.Contains(FileExtenstions[i], StringComparison.OrdinalIgnoreCase))
{
//exists on disk
if (File.Exists(Path.Combine(dir, path.Replace("/", #"\", StringComparison.Ordinal))))
return FileLocation.Disk;
//has a file as an embedded resource with the same name (ignores the path) so you might have duplicates names
if (resourceNames.Any(a => a.EndsWith(path.Split('/')[^1], StringComparison.OrdinalIgnoreCase)))
return FileLocation.Resource;
}
}
return FileLocation.NotSet;
}
after this you just do:
switch (IsMappedTo(url))
{
case FileLocation.NotSet:
break;
case FileLocation.Disk:
break;
case FileLocation.Resource:
break;
}

Categories