When uploading folders using the https://github.com/malsup/form plugin, files come to the server, the full path to which looks like this (on the server side, I get this information using HttpFileCollectionBase and FileInfo)

root/folder1/file1.txt root/folder2/file2.txt root/file3.txt 

Tell me, please, how to properly parse (approximate algorithm or which way to go) this information for correct reconstruction of the structure in the database

    1 answer 1

    The database structure is the simplest; it is a typical hierarchical directory table:

     Folder: ID ParentId ... 

    And the files in the directory:

     File: ID FolderId ... 

    In which Folder refers to itself, and File refers to Folder.

    enter image description here

    How to work with such a hierarchical structure can be found on the Internet on the topic of hierarchical data in the database or Common table expression (CTE). For example, you can take a little bit from here:

    http://club.shelek.ru/viewart.php?id=306

    Dividing the hierarchical structure of the directory separately and the files that refer to the directories separately, you can store them in a database with a more or less convenient method of access.

    For EntityFramefork, this might look something like this:

     [Table("Folder")] public class Folder { [Key] public int ID { get; set; } public int? ParentId { get; set; } public String Name { get; set; } public String Other { get; set; } [ForeignKey("ID")] public virtual Folder Parent { get; set; } [ForeignKey("ParentId")] public virtual List<Folder> Children { get; set; } [ForeignKey("FolderId")] public virtual List<File> FilesInFolder { get; set; } } [Table("File")] public class File { [Key] public int ID { get; set; } public int FolderId { get; set; } public String Name { get; set; } public Byte[] Bytes { get; set; } public String Other { get; set; } [ForeignKey("FolderId")] public virtual Folder Folder { get; set; } } public class Entities : DbContext { public Entities() : base("name=Entities") { this.Database.Initialize(true); } public DbSet<File> Files { get; set; } public DbSet<Folder> Folders { get; set; } } 

    Example of filling data:

      Entities entities = new Entities(); Folder f1 = new Folder() { Name = "SomeCatalog", Other = "Other", Children = new List<Folder>() { new Folder() { Name = "SomeCatalog", Other = "Other", Children = new List<Folder>() }, new Folder() { Name = "SomeCatalogWFile", Other = "Other", Children = new List<Folder>() { new Folder() { Name = "SomeCatalog", Other = "Other", Children = new List<Folder>() }, new Folder() { Name = "SomeCatalogWFileSub", Other = "Other", Children = new List<Folder>(), FilesInFolder = new List<EntityModel.File>(){ new EntityModel.File() { Name = "File1", Other = "TXT" }, new EntityModel.File() { Name = "File1", Other = "TXT" } } } }, FilesInFolder = new List<EntityModel.File>(){ new EntityModel.File() { Name = "File1", Other = "TXT" }, new EntityModel.File() { Name = "File1", Other = "TXT" } } } } }; entities.Folders.Add(f1); entities.SaveChanges(); 

    In your case, you still probably need to check whether the directory exists and whether there are any files to add in it. This is more complicated, but approximate code might look like this. Although this is far from ideal, as it was written on the knee, but for example, I think it will:

    Intermediate auxiliary class.

      class ParserPath { public Folder FolderPath { get; set; } public EntityModel.File FilePath { get; set; } public ParserPath(string path) { String[] PathRoad = Path.GetDirectoryName(path).Split('\\'); FolderPath = new Folder() { Name = PathRoad[0], Other = "Other", Children = new List<Folder>() }; Folder temp = FolderPath; for (int i = 1; i < PathRoad.Count(); i++) { Folder newTemp = new Folder { Name = PathRoad[i], Other = "Other", Children = new List<Folder>() }; temp.Children.Add(newTemp); temp = newTemp; } FilePath = new EntityModel.File() { Name = Path.GetFileName(path), Other = "TXT" }; } } 

    Parsing itself:

      List<ParserPath> parserPathList = new List<ParserPath>() { new ParserPath("root/file1.txt"), new ParserPath("root/folder2/file2.txt"), new ParserPath("root/folder1/file1.txt"), new ParserPath("root/folder1/file3.txt"), new ParserPath("root/folder1/root/file3.txt"), new ParserPath("root/folder1/root/file4.txt"), new ParserPath("root/folder2/folder1/file2.txt") }; //сохраняем структуру каталога foreach (ParserPath parserPath in parserPathList) { Folder current = entities.Folders.Select(p => p).Where(p => p.Name == parserPath.FolderPath.Name && p.ParentId == null).FirstOrDefault(); if (current == null) { entities.Folders.Add(parserPath.FolderPath); } else { Folder root = parserPath.FolderPath; while (root.Children.FirstOrDefault() != null) { Folder temp = current.Children.Select(p => p).Where(p => p.Name == root.Children.FirstOrDefault().Name).FirstOrDefault(); if (temp == null) { current.Children.Add(root.Children.FirstOrDefault()); break; } root = root.Children.FirstOrDefault(); current = temp; } } entities.SaveChanges(); } //предыдущий код изменяет коллекцию, что приводит к неправильным результатам. //Поэтому тут создается новая, что бы не переделывать код List<ParserPath> parserPathListCopy = new List<ParserPath>() { new ParserPath("root/file1.txt"), new ParserPath("root/folder2/file2.txt"), new ParserPath("root/folder1/file1.txt"), new ParserPath("root/folder1/file3.txt"), new ParserPath("root/folder1/root/file3.txt"), new ParserPath("root/folder1/root/file4.txt"), new ParserPath("root/folder2/folder1/file2.txt") }; //сохраняем файлы из каталога foreach (ParserPath parserPath in parserPathListCopy) { Folder current = entities.Folders.Select(p => p).Where(p => p.Name == parserPath.FolderPath.Name && p.ParentId == null).FirstOrDefault(); Folder root = parserPath.FolderPath; while (root.Children.FirstOrDefault() != null) { Folder temp = current.Children.Select(p => p).Where(p => p.Name == root.Children.FirstOrDefault().Name).FirstOrDefault(); root = root.Children.FirstOrDefault(); current = temp; } if (current.FilesInFolder == null) current.FilesInFolder = new List<EntityModel.File>(); current.FilesInFolder.Add(parserPath.FilePath); } entities.SaveChanges(); 

    As a result, you should get approximately the following structure in the database and with the usual SELECT from EF, simple data handling:

    enter image description here

    • entity file looks like this . This can be a file and folder. The fact that I can parse this way I know, but I don’t understand how I can maintain the structure exactly, i.e. root will be the parent for folder1, folder2, file3.txt, folder1 will be the parent for file1.txt, folder2 will be the parent for file2.txt. If the user wants to upload 100 files or more. - Andrii Albu
    • perhaps the only normal way would be to save everything on the server, and then read the directory tree - Andrii Albu
    • @AndriiAlbu, which database do you use in your work (MSSQL / etc) and which technology for queries (EF or pure SQL)? - Alex Krass
    • Data Source = (localdb) \ MSSQLLocalDB, providerName = "System.Data.SqlClient", Entity Framework Technology - Andrii Albu
    • @AndriiAlbu, I updated the answer, look, try to figure it out. Although the answer was not quite simple due to the size and consideration of both the database and EF with the example of the parsing code. - Alex Krass