I'm having trouble deleting a folder with all files in it.
I get this error:
Could not find a part of the path
What I'm trying to accomplish is, getting the relative path from the database, and then deleting that folder with all files in it.
Here is the code:
public IActionResult RemoveCar(string item)
{
var car = _context.CarModels.Where(x => x.Id.ToString() == item).FirstOrDefault();
var pictures = _context.Pictures.Where(x => x.CarModelId.ToString() == item).ToList();
if(pictures.Count() > 0 && pictures != null)
{
string parent = new System.IO.DirectoryInfo(pictures[0].Path).Parent.ToString();
string lastFolderName = Path.GetFileName(Path.GetDirectoryName(parent+"/"));
string exactPath = Path.GetFullPath("/images/" + lastFolderName);
System.IO.DirectoryInfo di = new System.IO.DirectoryInfo(exactPath);
// Delete this dir and all subdirs.
try
{
di.Delete(true);
}
catch (System.IO.IOException e)
{
Console.WriteLine(e.Message);
}
foreach (var pic in pictures)
{
_context.Pictures.Remove(pic);
}
}
_context.CarModels.Remove(car);
return RedirectToAction("RemoveCar");
}
I think the first slash in this line is the problem,
string exactPath = Path.GetFullPath("/images/" + lastFolderName);
as it means 'move to the root'. Leave it out if you want a relative path.
Related
I have folder objects that are stored in a way that I can see which folder is in which other folder. These objects have id, name and parentIdattributes. Each folder's parentId is the id of another folder. The root folder's parent Id is null.
I have a form which allows to create new folders. The form takes a name (string) and a parent (string) input. The name can be anything, but the parent must be in the format of existing folders: "Documents|Books|Fiction", etc. If there are no folders existing already, to create the initial Documents folder, the parent can be empty.
I am struggling in finding the algorithm to ensure a new folder is mapped in the correct folder given the file path of parent. For example, if the folder structure looks like the following:
Documents|Holidays
Documents|Books|Holidays
Documents|Jobs|Books|Holidays
And lets say that I am creating a new folder with name "My Day in Paris" and parent "Documents|Books|Holidays" In the controller method for CreateFolder, before I save the new Folder object to the database, I need to give this Folder object the ParentId of the parent folder. In our case the parent is Holidays folder which has the path "Documents|Books|Holidays".
For example,I could query the database (pseudocode):
db.Folders.where(x => x.Name == "Holidays").Select(y => y.Id).single(); But this will not get me the right folder from the file path to set the correct ParentId.
How can I get the parent Id in this way? I tried searching around for ages and tried to work out an algorithm but no luck.
Update with my own attempt. It reaches the correct value to return, but instead of returning this value, it continues back to the previously paused recursive cycles. How do I break with the output value without it going back?
public int GetIdFromPath(string path)
{
int _parentId = 0;
var folderStructure = GetFolderStructure();
if (path != null)
{
_parentId = recursiveFunction(path, folderStructure);
return _parentId;
}
return _parentId;
}
private int recursiveFunction(string path, List<Folder> folders)
{
var splitPath = path.Split('|');
var _parentId = 0;
foreach (var item in splitPath)
{
try
{
foreach (var folder in folders)
{
if (folder.Name == item)
{
splitPath = splitPath.Where(x => x != item).ToArray();
_parentId = folder.Id;
if (splitPath.Count() > 0)
{
var newPath = string.Join("|", splitPath);
recursiveFunction(newPath, folder.FolderBookmarks.OfType<Folder>().ToList());
}
return _parentId;
}
}
}
catch (Exception e)
{
return _parentId;
}
}
return _parentId;
}
public List<Folder> GetFolders()
{
var folders = db.Bookmarks.OfType<Folder>().ToList();
return folders;
}
public List<Folder> GetFolderStructure()
{
var folders = GetFolders();
foreach (var folder in folders)
{
var bookmarks = db.Bookmarks.Where(x => x.ParentId == folder.Id).ToList();
folder.FolderBookmarks = bookmarks;
}
return folders;
}
You must find each parent step by step like this and also handle exceptions:
int? _parentId = null;
foreach (var item in path.Split('|'))
{
var folder = db.Folders
.where(x => x.Name == item&&x.ParentId = _parentId)
.Select(y => y.Id)
.Single();
_parentId = folder.Id;
}
return _parentId;
I have come up with the solution. The variable folders is a list of folder objects. (Note: Folders have many folders). The code below sets the parent ID as the folder ID of the folder name that matches the path name. The folders list then gets set to that folder's internal list of folders; this will allow to look within the folder. (Also note that we do not allow duplicate folder names in this context). Thanks Reza, your comment helped!
public int GetParentIdFromPath(string path)
{
int _parentId = 0;
if (path != null)
{
var folders = GetFolderStructure();
var splitPath = path.Split('|');
foreach (var item in splitPath)
{
_parentId = folders.Where(x => x.Name == item).SingleOrDefault().Id;
folders = folders.Where(x => x.Name == item).SingleOrDefault().FolderBookmarks.OfType<Folder>().ToList();
}
return _parentId;
}
return _parentId;
}
I tried to extract all my files and folders starting in c:\ to a tree data structure.
I get an UnAuthorized exception for some directories.
Is there another solution to check to see if I have permission to a folder?
I also tried using directoryName.Exists - it did not work.
my code:
private static void getAllFilesAndFoldersInPath(TreeView treeView, string path)
{
treeView.Items.Clear();
var stack = new Stack<TreeViewItem>();
var rootDirectory = new DirectoryInfo(path);
TreeViewItem node = new TreeViewItem();
node.Header = rootDirectory;
stack.Push(node);
while (stack.Count > 0)
{
var currentNode = stack.Pop();
var directoryInfo = (DirectoryInfo)currentNode.Header;
try
{
foreach (var directory in directoryInfo.GetDirectories())
{
if (!directory.Name.StartsWith("$"))
{
TreeViewItem childDirectoryNode = new TreeViewItem();
childDirectoryNode.Header = directory;
currentNode.Items.Add(childDirectoryNode);
stack.Push(childDirectoryNode);
}
}
foreach (var file in directoryInfo.GetFiles())
{
if (System.IO.Path.GetFileName(file.Name).StartsWith("~$") || System.IO.Path.GetExtension(file.Name) == ".tmp")
{
continue;
}
TreeViewItem tempNode = new TreeViewItem();
tempNode.Header = file;
currentNode.Items.Add(tempNode);
}
}
catch (UnauthorizedAccessException e) { }
}
treeView.Items.Add(node);
}
I would use EnumerateDirectories instead of GetDirectories. Much, much faster.
In your catch statement - do something with the exception. Don't just let the code swallow it, this is bad programming.
I have a handy little method I wrote awhile back that works well for me, feel free to give it a spin:
public IEnumerable<T> Find<T>(DirectoryInfo workingDirectory,
string searchPattern,
bool recursive = false)
where T : FileSystemInfo
{
var results = workingDirectory.EnumerateFileSystemInfos(searchPattern,
recursive ? SearchOption.AllDirectories : SearchOption.TopDirectoryOnly);
return results.OfType<T>();
}
Where in your case you might call it like this:
var dirTree = Find<DirectoryInfo>(new DirectoryInfo("C:\\"), "*", true);
I have a folder containing .txt files which are numbered like so:
0.txt
1.txt
...
867.txt
...
What I am trying to do is each time readNextFile(); is called, I want it to return the contents of the next file in that folder, and return string.Empty; if there is no file after the last one it read. I want to have a button that, when pressed, will make the program read the next file and do stuff with it's contents. The files might change between button presses. The way I did this before was this:
int lastFileNumber = 0;
string readNextFile()
{
string result = string.Empty;
//I know it is recommended to use as few of these as possible, this is just an example.
try
{
string file = Path.Combine("C:\Somewhere", lastFileNumber.ToString() + ".txt");
if (File.Exists(file))
{
result = File.ReadAllText(file);
lastFileNumber++;
}
}
catch
{
}
return result;
}
Problem is there might sometimes be this kind of situation:
0.txt
1.txt
5.txt
6.txt
...
It would obviously get stuck at 1.txt because 2.txt doesn't exist. I need it to skip to the next existing file and read that one. And clearly it is not possible to just sort the file names alphabetically in a string array since the file names are not Padded, so doing that will result in 1000000000.txt being read right after 1.txt.
Any idea how I can achieve this?
You can use linq to check the next file based on the stored number. That is done after ordering the file by converting its name into integer representation:
int lastFileNumber = -1;
bool isFirst = true;
private void buttonNext_Click(object sender, EventArgs e)
{
int lastFileNumberLocal = isFirst ? -1 : lastFileNumber;
isFirst = false;
int dummy;
var currentFile = Directory.GetFiles(#"D:\", "*.txt", SearchOption.TopDirectoryOnly)
.Select(x => new { Path = x, NameOnly = Path.GetFileNameWithoutExtension(x) })
.Where(x => Int32.TryParse(x.NameOnly, out dummy))
.OrderBy(x => Int32.Parse(x.NameOnly))
.Where(x => Int32.Parse(x.NameOnly) > lastFileNumberLocal)
.FirstOrDefault();
if (currentFile != null)
{
lastFileNumber = Int32.Parse(currentFile.NameOnly);
string currentFileContent = File.ReadAllText(currentFile.Path);
}
else
{
// reached the end, do something or show message
}
}
I don't think you can find what file is last without getting the whole list of files first. The sorting can be simplified by sorting by the file name length and then by the file name.
int currentFileNumber = -1;
string currentFileName;
string currentFileText;
string[] allFileNames;
string readCurrentFile()
{
try
{
if (allFileNames == null) allFileNames = (
from f in Directory.EnumerateFiles(#".", "*.*")
orderby f.Length, f select f).ToArray();
currentFileNumber++;
if (currentFileNumber >= allFileNames.Length) return null; // no files left
currentFileName = allFileNames[currentFileNumber];
currentFileText = File.ReadAllText(currentFileName);
return currentFileText;
}
catch (Exception ex) {
MessageBox.Show(ex.Message);
return readCurrentFile(); // get next file if any Exception
}
}
I am currently using Renci SSH.NET to upload files and folders to a Unix Server using SFTP, and creating directories using
sftp.CreateDirectory("//server/test/test2");
works perfectly, as long as the folder "test" already exists. If it doesn't, the CreateDirectory method fails, and this happens everytime when you try to create directories containing multiple levels.
Is there an elegant way to recursively generate all the directories in a string? I was assuming that the CreateDirectory method does that automatically.
There's no other way.
Just iterate directory levels, testing each level using SftpClient.GetAttributes and create the levels that do not exist.
static public void CreateDirectoryRecursively(this SftpClient client, string path)
{
string current = "";
if (path[0] == '/')
{
path = path.Substring(1);
}
while (!string.IsNullOrEmpty(path))
{
int p = path.IndexOf('/');
current += '/';
if (p >= 0)
{
current += path.Substring(0, p);
path = path.Substring(p + 1);
}
else
{
current += path;
path = "";
}
try
{
SftpFileAttributes attrs = client.GetAttributes(current);
if (!attrs.IsDirectory)
{
throw new Exception("not directory");
}
}
catch (SftpPathNotFoundException)
{
client.CreateDirectory(current);
}
}
}
A little improvement on the code provided by Martin Prikryl
Don't use Exceptions as a flow control mechanism. The better alternative here is to check if the current path exists first.
if (client.Exists(current))
{
SftpFileAttributes attrs = client.GetAttributes(current);
if (!attrs.IsDirectory)
{
throw new Exception("not directory");
}
}
else
{
client.CreateDirectory(current);
}
instead of the try catch construct
try
{
SftpFileAttributes attrs = client.GetAttributes(current);
if (!attrs.IsDirectory)
{
throw new Exception("not directory");
}
}
catch (SftpPathNotFoundException)
{
client.CreateDirectory(current);
}
Hi I found my answer quite straight forwared. Since I found this old post, I thought others might also stumble upon it. The accepted answer is not that good, so here is my take. It does not use any counting gimmicks, so I think it's a little more easy to understand.
public void CreateAllDirectories(SftpClient client, string path)
{
// Consistent forward slashes
path = path.Replace(#"\", "/");
foreach (string dir in path.Split('/'))
{
// Ignoring leading/ending/multiple slashes
if (!string.IsNullOrWhiteSpace(dir))
{
if(!client.Exists(dir))
{
client.CreateDirectory(dir);
}
client.ChangeDirectory(dir);
}
}
// Going back to default directory
client.ChangeDirectory("/");
}
FWIW, here's my rather simple take on it. The one requirement is that the server destination path is seperated by forward-slashes, as is the norm. I check for this before calling the function.
private void CreateServerDirectoryIfItDoesntExist(string serverDestinationPath, SftpClient sftpClient)
{
if (serverDestinationPath[0] == '/')
serverDestinationPath = serverDestinationPath.Substring(1);
string[] directories = serverDestinationPath.Split('/');
for (int i = 0; i < directories.Length; i++)
{
string dirName = string.Join("/", directories, 0, i + 1);
if (!sftpClient.Exists(dirName))
sftpClient.CreateDirectory(dirName);
}
}
HTH
A little modification on the accepted answer to use spans.
It's probably utterly pointless in this case, since the overhead of the sftp client is far greater than copying strings, but it can be useful in other similiar scenarios:
public static void EnsureDirectory(this SftpClient client, string path)
{
if (path.Length is 0)
return;
var curIndex = 0;
var todo = path.AsSpan();
if (todo[0] == '/' || todo[0] == '\\')
{
todo = todo.Slice(1);
curIndex++;
}
while (todo.Length > 0)
{
var endOfNextIndex = todo.IndexOf('/');
if (endOfNextIndex < 0)
endOfNextIndex = todo.IndexOf('\\');
string current;
if (endOfNextIndex >= 0)
{
curIndex += endOfNextIndex + 1;
current = path.Substring(0, curIndex);
todo = path.AsSpan().Slice(curIndex);
}
else
{
current = path;
todo = ReadOnlySpan<char>.Empty;
}
try
{
client.CreateDirectory(current);
}
catch (SshException ex) when (ex.Message == "Already exists.") { }
}
}
my approach is more sufficient and easier to read and maintain
public static void CreateDirectoryRecursively(this ISftpClient sftpClient, string path)
{
// Consistent forward slashes
var separators = new char[] { Path.AltDirectorySeparatorChar, Path.DirectorySeparatorChar };
string[] directories = path.Split(separators);
string currentDirectory = "";
for (int i = 1; i < directories.Length; i++)
{
currentDirectory = string.Join("/", currentDirectory, directories[i]);
if (!sftpClient.Exists(currentDirectory))
{
sftpClient.CreateDirectory(currentDirectory);
}
}
}
I Need to find my pictures in my User folder. But I get the runtime error Access Denied
Here is my code
static void Main(string[] args)
{
string pic = "*.jpg";
string b = Environment.GetFolderPath(Environment.SpecialFolder.UserProfile);
string appdata = Path.Combine(b, "AppData"); // I Dont want search in this folder.
string data = Path.Combine(b, "Data aplikací"); // Here also not.
foreach (string d in Directory.GetDirectories(b))
{
try
{
if ((d == data) || (d == appdata))
{
continue;
}
else
{
foreach (string f in Directory.GetFiles(d, pic))
{
//...
}
}
}
catch (System.Exception excpt)
{
Console.WriteLine(excpt.Message);
}
}
}
Running the application as admin doesn't work either. How to avoid this?
check if the folder is read only (in windows) if it is, just clear the read only flag.
if it isn't read only, make sure that the admin user has full rights on that folder. You can check this by right clicking on the folder --> properties --> security
check out this link for more information on how to set it programatically:
C# - Set Directory Permissions for All Users in Windows 7
Oh, don't go changing your directory/folder permissions - that's just asking for future pain.
There's no "one-liner" solution here - basically, you need to recursively walk through the folder structure looking for the files you care about, and absorbing/eating the UnauthorizedAccessExceptions along the way (you could avoid the exception altogether by checking DirectoryInfo.GetAccessControl, but that's a whole different question)
Here's a blob o'code:
void Main()
{
var profilePath = Environment
.GetFolderPath(Environment.SpecialFolder.UserProfile);
var imagePattern = "*.jpg";
var dontLookHere = new[]
{
"AppData", "SomeOtherFolder"
};
var results = new List<string>();
var searchStack = new Stack<string>();
searchStack.Push(profilePath);
while(searchStack.Count > 0)
{
var path = searchStack.Pop();
var folderName = new DirectoryInfo(path).Name;
if(dontLookHere.Any(verboten => folderName == verboten))
{
continue;
}
Console.WriteLine("Scanning path {0}", path);
try
{
var images = Directory.EnumerateFiles(
path,
imagePattern,
SearchOption.TopDirectoryOnly);
foreach(var image in images)
{
Console.WriteLine("Found an image! {0}", image);
results.Add(image);
}
var subpaths = Directory.EnumerateDirectories(
path,
"*.*",
SearchOption.TopDirectoryOnly);
foreach (var subpath in subpaths)
{
searchStack.Push(subpath);
}
}
catch(UnauthorizedAccessException nope)
{
Console.WriteLine("Can't access path: {0}", path);
}
}
}