Article Overview
- Background
- Prerequisites
- Upload or download files from Network Drive in MVC C#
- Output
- Summary
Background
It is difficult to manage physical files when an application is being hosted on multiple servers. Because it might be possible that one is requesting to server 1 and the file might be stored at server 2. Hence, file not found issue may arise.
There are multiple ways to upload File on multiple servers. One of the simple ways is Shared Storage which I have covered in this article. In this approach, files are saved to the network share accessible by both servers. In load balanced environment, this approach may provide a single point of failure if the server providing the files is not available.
Prerequisites
- You should have basic knowledge of ASP.NET MVC and JavaScript/jQuery.
- Before start create one folder and give access rights to that folder for a specific user which we will use in code.
Upload or download files from Network Drive in MVC C#
Here, there is one view with JS code and one controller code. Also, I have attached the code with this article.
To accessing the network drive, we are using advapi32.dll and kernel32.dll library for accessing files and the directory of the network folder. To access the network drive, we have to pass the network path, username, password and domain to connect with the network. Let's start coding.
View side HTML code - to upload and display clickable files UI.
- File upload control to select files for the upload using the save button event
- File list div to list out all files and allows to click to download the particular file
<h2>Upload Files</h2>
<div class="row">
<div class="col-sm-4">
<input id="fuFileUpload" type="file" name="Choose File" multiple />
</div>
<div class="col-sm-4">
<input type="button" id="btnSave" title="Save" value="Save" onclick="funSave()" />
</div>
</div>
<h2>Uploaded File List</h2>
<div class="row">
<div class="col-sm-4">
<strong>File Name</strong>
</div>
</div>
<div id="divFiles"></div>
View side JavaScript/jQuery code - to upload and display clickable files UI to the Controller interactions.
- The file save function will call the controller method to save the actual selected files
- File list function to list out all files and clickable
- The open function will download a clicked file to view it
<script src="https://code.jquery.com/jquery-1.9.1.min.js"></script>
<script>
function funSave() {
var formData = new FormData();
var files = $('#fuFileUpload').get(0).files;
$(files).each(function (index, element) {
formData.append("importFile[" + index + "]", files[index]);
});
$.ajax({
type: 'POST',
data: formData,
url: 'Create',
contentType: false,
datatype: "JSON",
processData: false,
success: function (response) {
alert('Files saved successfully!');
funFileList();
},
error: function (jqXhr, textStatus, errorMessage) {
alert(errorMessage);
}
});
}
function funFileList() {
$.ajax({
type: 'GET',
url: 'FileList',
contentType: false,
datatype: "JSON",
processData: false,
success: function (response) {
$('#divFiles').empty();
$.each(response, function (key, val) {
$('#divFiles').append(`
<a href="#" id='2' onclick='funOpen("` + val + `");'>${val}</a></br>
`);
});
},
error: function (jqXhr, textStatus, errorMessage) {
alert(errorMessage);
}
});
}
function funOpen(fileName) {
$.ajax({
type: 'POST',
url: 'Download?fileName=' + fileName,
contentType: false,
datatype: "JSON",
processData: false,
xhrFields: {
responseType: 'blob'
},
success: function (response) {
var a = document.createElement('a');
var url = window.URL.createObjectURL(response);
a.href = url;
a.download = fileName;
document.body.append(a);
a.click();
a.remove();
window.URL.revokeObjectURL(url);
},
error: function (jqXhr, textStatus, errorMessage) {
alert(errorMessage);
}
});
}
$(function () {
funFileList();
});
</script>
Controller side Action methods code - to interact from an ajax call.
- Create a method to save in the shared folder using the access credentials of the selected files.
- File list to return the array of file names.
- Download will return selected file as file return type.
[HttpPost]
public ActionResult Create(FileUploadViewModel objFileUploadViewModel)
{
UploadFile(objFileUploadViewModel.importFile);
return Json(new { Success = "true" }, JsonRequestBehavior.AllowGet);
}
[HttpGet]
public ActionResult FileList()
{
return Json(GetFiles(), JsonRequestBehavior.AllowGet);
}
[HttpPost]
public ActionResult Download(string fileName)
{
return File(DownloadFile(fileName), "application/vnd.ms-excel", fileName);
}
Controller side NonAction methods code - actual download or upload operation using user authentication.
- Get file method connects with the shared folder with authentication and prepares the file list and returns as an array.
- Upload file connects with the shared folder with authentication and saves the file to the shared folder.
- Download file method connects with the shared folder with authentication and returns the given file as a byte array.
[NonAction]
public string[] GetFiles()
{
string filepath = @"\\machinename\filefolder\";
string networkFileUser = @"username";
string networkFilePassword = @"userpassword";
string networkFileDomain = @"domainname";
var userToken = IntPtr.Zero;
string[] files = new string[0];
int i = 0;
try
{
var isSuccess = LogonUser(networkFileUser, networkFileDomain, networkFilePassword, 2, 0, ref userToken);
if (!isSuccess)
{
throw new System.ComponentModel.Win32Exception(System.Runtime.InteropServices.Marshal.GetLastWin32Error());
}
System.Security.Principal.WindowsIdentity windowsIdentity = new System.Security.Principal.WindowsIdentity(userToken);
System.Security.Principal.WindowsIdentity.RunImpersonated(windowsIdentity.AccessToken, () =>
{
var fileList = System.IO.Directory.GetFiles(filepath);
files = new string[fileList.Length];
foreach (var file in System.IO.Directory.GetFiles(filepath))
{
files[i++] = file.ToString().Replace(filepath, "");
}
});
}
catch (Exception ex)
{
}
finally
{
if (userToken != IntPtr.Zero)
{
CloseHandle(userToken);
}
}
return files;
}
[NonAction]
public void UploadFile(List<HttpPostedFileBase> importFile)
{
string filepath = @"\\machinename\filefolder\";
string networkFileUser = @"username";
string networkFilePassword = @"userpassword";
string networkFileDomain = @"domainname";
var userToken = IntPtr.Zero;
try
{
if (importFile != null)
{
var isSuccess = LogonUser(networkFileUser, networkFileDomain, networkFilePassword, 2, 0, ref userToken);
if (!isSuccess)
{
throw new System.ComponentModel.Win32Exception(System.Runtime.InteropServices.Marshal.GetLastWin32Error());
}
System.Security.Principal.WindowsIdentity windowsIdentity = new System.Security.Principal.WindowsIdentity(userToken);
System.Security.Principal.WindowsIdentity.RunImpersonated(windowsIdentity.AccessToken, () =>
{
foreach (var image in importFile)
{
image.SaveAs(filepath + image.FileName);
}
});
}
}
catch (Exception ex)
{
}
finally
{
if (userToken != IntPtr.Zero)
{
CloseHandle(userToken);
}
}
}
[NonAction]
public byte[] DownloadFile(string fileName)
{
string filepath = @"\\machinename\filefolder\";
string networkFileUser = @"username";
string networkFilePassword = @"userpassword";
string networkFileDomain = @"domainname";
byte[] fileBytes = null;
var userToken = IntPtr.Zero;
try
{
var isSuccess = LogonUser(networkFileUser, networkFileDomain, networkFilePassword, 2, 0, ref userToken);
if (!isSuccess)
{
throw new System.ComponentModel.Win32Exception(System.Runtime.InteropServices.Marshal.GetLastWin32Error());
}
System.Security.Principal.WindowsIdentity windowsIdentity = new System.Security.Principal.WindowsIdentity(userToken);
System.Security.Principal.WindowsIdentity.RunImpersonated(windowsIdentity.AccessToken, () =>
{
fileBytes = System.IO.File.ReadAllBytes(filepath + fileName);
});
}
catch (Exception ex)
{
}
finally
{
if (userToken != IntPtr.Zero)
{
CloseHandle(userToken);
}
}
return fileBytes;
}
Controller side other/dll methods code - for actual user authentication and close handle.
- LogonUser method of advapi32.dll for connection establishment.
- CloseHandle of kernel32.dll to close the handle.
[System.Runtime.InteropServices.DllImport("advapi32.dll", SetLastError = true, CharSet = System.Runtime.InteropServices.CharSet.Auto)]
static extern bool LogonUser(
string lpszUsername,
string lpszDomain,
string lpszPassword,
int dwLogonType,
int dwLogonProvider,
ref IntPtr phToken
);
[System.Runtime.InteropServices.DllImport("kernel32.dll", CharSet = System.Runtime.InteropServices.CharSet.Auto)]
static extern bool CloseHandle(IntPtr handle);
Output
Start the application and it will show file upload option, and there will be no file in the file list.
![Run Application]()
Image 1: File selection and file listing at file run time
Upload file: click on Choose Files option, select files, and then click on "Save" button to upload file.
![Upload file]()
Image 2: Click on the Choose Files option and it will open the file selection popup, select the particular file(s)
![Choose file]()
Image 3: After file selection, click on the Save button to perform actual file upload
![Save button]()
Image 4: After the file successfully uploads, a success message will be displayed, and the file will be named in the File Name list
After saving/the file will be saved to the shared folder and displayed in the file list option, and from there, by clicking the particular file, it can be downloaded.
![File Upload]()
Image 5: From the File Name list, click on the particular file, and it will be downloaded to view
Summary
Here, the basic concept is being implemented for the high-level concept clarity purpose. In actual application development, various logic can be incorporated, such as file type, file size, etc., constraints, also file save with a unique name instead of given names, etc.
Now, I believe you will be able to upload and download files from Network Drive or Shared Storage using MVC application in C#.
For more details, one can refer to the below articles.