Introduction
I had about 3000 PDF, ZIP and JPG files in too many folders on my server and wanted to simply make hyperlinks for exploring them on server in my site template and users can download the files in folders. This tip will show how you can easily make hyperlinks for exploring a folder on server and its subdirectories and downloading the files, using ASP.NET MVC.
Using the Code
This demo uses a default ASP.NET MVC 4 project.
We need to add Explorer Controller and its Index View, add ExplorerModel.cs, some edit in RouteConfig.cs and also add "Folder" that contain the files and folders for exploring and downloading.
Explorer Model
First of all, we need a model. So we add ExplorerModel.cs to Models. This model contains three classes. You could add every property that you want the classes to contain. In this demo, we made some simple models.
A class for DirModel
that contains a directory information.
public class DirModel
{
public string DirName { get; set; }
public DateTime DirAccessed { get; set; }
}
A class for FileModel
that contains file information.
public class FileModel
{
public string FileName { get; set; }
public string FileSizeText { get; set; }
public DateTime FileAccessed { get; set; }
}
A class for ExplorerModel
that contains directories and files list.
public class ExplorerModel
{
public List<DirModel> dirModelList;
public List<FileModel> fileModelList;
public ExplorerModel(List<DirModel> _dirModelList, List<FileModel> _fileModelList)
{
dirModelList = _dirModelList;
fileModelList = _fileModelList;
}
}
Explorer Controller
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using FolderExplorer.Models;
using System.IO;
namespace FolderExplorer.Controllers
{
public class ExplorerController : Controller
{
public ActionResult Index(string path)
{
string realPath;
realPath = Server.MapPath("~/Folder/" + path);
if (System.IO.File.Exists(realPath))
{
return base.File(realPath, "application/octet-stream");
}
else if (System.IO.Directory.Exists(realPath))
{
Uri url = Request.Url;
if (url.ToString().Last() != '/')
{
Response.Redirect("/Explorer/" + path + "/");
}
List<FileModel> fileListModel = new List<FileModel>();
List<DirModel> dirListModel = new List<DirModel>();
IEnumerable<string> dirList = Directory.EnumerateDirectories(realPath);
foreach (string dir in dirList)
{
DirectoryInfo d = new DirectoryInfo(dir);
DirModel dirModel = new DirModel();
dirModel.DirName = Path.GetFileName(dir);
dirModel.DirAccessed = d.LastAccessTime;
dirListModel.Add(dirModel);
}
IEnumerable<string> fileList = Directory.EnumerateFiles(realPath);
foreach (string file in fileList)
{
FileInfo f = new FileInfo(file);
FileModel fileModel = new FileModel();
if (f.Extension.ToLower() != "php" && f.Extension.ToLower() != "aspx"
&& f.Extension.ToLower() != "asp")
{
fileModel.FileName = Path.GetFileName(file);
fileModel.FileAccessed = f.LastAccessTime;
fileModel.FileSizeText = (f.Length < 1024) ?
f.Length.ToString() + " B" : f.Length / 1024 + " KB";
fileListModel.Add(fileModel);
}
}
ExplorerModel explorerModel = new ExplorerModel(dirListModel, fileListModel);
return View(explorerModel);
}
else
{
return Content(path + " is not a valid file or directory.");
}
}
}
}
As you can see, ExplorerController
has only one action. Index
action gets the path
parameter.
public ActionResult Index(string path)
How can we use path
as UrlParameter
?
If we browse "http://localhost:45114/Explorer/", we will see the files and directories in "Folder" and if we browse "http://localhost:45114/Explorer/Test%20Folder%201/Folder1/", we will see the files and directories in "Folder\Test Folder 1\Folder1". Because everything after "Explorer/" is our path
and path
contains some slashes and maybe spaces, so we can use URL: "Explorer/{*path}" in Explorer routes.MapRoute
. So we must add the code below to RouteConfig.cs. See this for custom routes and MVC URLs with slash in parameter.
routes.MapRoute(
name: "Explorer",
url: "Explorer/{*path}",
defaults: new { controller = "Explorer",
action = "Index",
path = UrlParameter.Optional }
);
The following code will define the folder, that you want to use for exploring, you could use any method that you prefer to define this, like using Web.Config
, Injection or ... It's not important. This code is just used for demo. See this for Server.MapPath.
string realPath;
realPath = Server.MapPath("~/Folder/" + path);
Now we will check if the realPath is a file that exists. If it exists, we will send the contents of file as the response by using "return base.File(realPath, "application/octet-stream")
". See this for more information about File. We will use "application/octet-stream
" as contentType
(See this).
if (System.IO.File.Exists(realPath))
{
return base.File(realPath, "application/octet-stream");
}
If realPath wasn't a file, then we check if it's a directory or not.
else if (System.IO.Directory.Exists(realPath))
{
.
.
.
return View(explorerModel);
}
else
{
return Content(path + " is not a valid file or directory.");
}
For using browser ability to correctly browse the folders, every path needs to end with slash. The following codes do these:
Uri url = Request.Url;
if (url.ToString().Last() != '/')
{
Response.Redirect("/Explorer/" + path + "/");
}
Then we need to define the list that contains DirModels
:
List<DirModel> dirListModel = new List<DirModel>();
IEnumerable<string> dirList = Directory.EnumerateDirectories(realPath);
foreach (string dir in dirList)
{
DirectoryInfo d = new DirectoryInfo(dir);
DirModel dirModel = new DirModel();
dirModel.DirName = Path.GetFileName(dir);
dirModel.DirAccessed = d.LastAccessTime;
dirListModel.Add(dirModel);
}
Also, we need to define the list that contains FileModels
:
List<FileModel> fileListModel = new List<FileModel>();
IEnumerable<string> fileList = Directory.EnumerateFiles(realPath);
foreach (string file in fileList)
{
FileInfo f = new FileInfo(file);
FileModel fileModel = new FileModel();
if (f.Extension.ToLower() != "php" && f.Extension.ToLower() != "aspx"
&& f.Extension.ToLower() != "asp")
{
fileModel.FileName = Path.GetFileName(file);
fileModel.FileAccessed = f.LastAccessTime;
fileModel.FileSizeText =
(f.Length < 1024) ? f.Length.ToString() + " B" : f.Length / 1024 + " KB";
fileListModel.Add(fileModel);
}
}
Finally, we return explorerModel
to Explorer Index View.
ExplorerModel explorerModel = new ExplorerModel(dirListModel, fileListModel);
return View(explorerModel);
Explorer Index View
The description included in code:
@{
ViewBag.Title = "Folder Explorer";
}
@{Uri uri = Request.Url;}
@**@
<h2>@Server.UrlDecode(uri.Segments.Last())</h2>
@**@
@if (uri.AbsolutePath.ToLower() != "/explorer/")
{
@**@
<a title="Parent"
href="@uri.AbsoluteUri.Remove(uri.AbsoluteUri.Length - uri.Segments.Last().Length)">
<img src="http://www.codeproject.com/Content/up.png"
alt="Up" style="width: 20px; height: 20px; border: none" />
</a>
}
<ul>
@**@
@foreach (FolderExplorer.Models.DirModel dir in Model.dirModelList)
{
<li>
<img src="http://www.codeproject.com/Content/folder.png"
alt="Folder Logo" align="top" style="width: 20px;
height: 20px; border: none" />
<a href="@dir.DirName/" title="@dir.DirName">@dir.DirName</a>
. . . . . . @dir.DirAccessed
</li>
}
@**@
@foreach (FolderExplorer.Models.FileModel file in Model.fileModelList)
{
<li><a href="@(uri.AbsolutePath + file.FileName)"
title="@file.FileName" target="_blank">
@file.FileName</a>. . . . . . @file.FileSizeText
. . . . . . @file.FileAccessed
</li>
}
</ul>
Related Tip
You can find the .NET Core version here.
Summary
This demo worked for me and it's serving the users. Hope it helps you too. Please don't forget to mark your votes, suggestions and feedback to improve the quality of this tip.