Download files with absolute path outside of wwwroot with Razor Page - c#

We are building a Razor Page WebApp that we need our users to be able to download files stored on our server.
File path looks like this
//server_name/Databases/PDF_Files/2022-01-01-Invoice.pdf
All the files are on our server. When I put the path in the browser, I am able to view it in the browser.
With the absolute path available, what would be the best way to implement this?
A simple code snippet would be much appreciated.

You can use File.ReadAllBytes to get a byte array, and then return that as a FileResult:
public async Task<FileResult> OnGetFile(string filename)
{
var path = #"\\server_name\Databases\PDF_Files";
var file = Path.Combine(path, filename);
return File(await File.ReadAllBytesAsync(file), "application/pdf", filename);
}
The URL for the named handler method above would need to include a query string that has the handler name ("file") and the file name e.g.:
?handler=file&filename=2022-01-01-Invoice.pdf
Obviously, you will need to validate the file name to ensure that it exists and doesn't attempt to access other directories on the server, and you will need to ensure that the account that the web app process runs under has sufficient privileges to access the network resource.

Related

Create file during mstest - System.UnauthorizedAccessException

I have a UWP C# app, with a unit testing project. In these unit test, I want to be able to write to a text file in order to make something like snapshots in Jest.
Directory.GetCurrentDirectory() returns C:\path\to\project\bin\x64\Debug\AppX, so I made a folder in the project directory and am navigating to it, then attempting to create a file there.
[TestMethod]
public void Test()
{
var folder = Path.Combine(Directory.GetCurrentDirectory(), "../../../../Snapshots");
string data = "example data";
string filename = Path.Combine(folder, "Test.snap");
File.WriteAllText(filename, json);
}
However, this test produces a System.UnauthorizedAccessException. I went into the folder in windows and gave Everyone read/write permissions, but that didn't make any difference.
I don't want to have to run Visual Studio as an administrator. Is this possible?
I use Path.GetTempPath() to create temporary directories and files in unit tests that require physical disk access. The unit tests can run from an unknown context/location, so I found using the temp directory as a guaranteed way to create disposable files.
[TestMethod]
public void Test()
{
var folder = Path.Combine(Path.GetTempPath(), "Snapshots");
string data = "example data";
string filename = Path.Combine(folder, "Test.snap");
File.WriteAllText(filename, json);
}
Please have a look at Rob's blog here:
https://blogs.msdn.microsoft.com/wsdevsol/2012/12/04/skip-the-path-stick-to-the-storagefile/
Here is the answer from Rob:
Windows Store apps run sandboxed and have very limited access to the
file system. For the most part, they can directly access only their
install folder and their application data folder. They do not have
permission to access the file system elsewhere (see File access and
permissions for more details).
Access to other locations is available only through a broker process.
This broker process runs with the user’s full privileges, and it can
use these privileges on the app’s behalf for locations the app has
requested via capabilities, locations requested by the user via file
pickers, etc. The StorageItem encapsulates this brokerage procedure so
the app doesn’t need to deal with it directly."
In a UWP app we do not recommend path anymore. There are permission problems so broker is required when access some paths. I'm not familar with Unit Test. But if you are still using UWP function you should consider using StorageFile related API instead.
How about checking if you gave permissions to the right folder?
var folder = Path.Combine(Directory.GetCurrentDirectory(), "../../../../Snapshots");
string data = "example data";
// this variable will contain the actual folder; add a watch
// or bookmark it to check it
var actualPath = Path.GetFullPath(folder);
string filename = Path.Combine(folder, "Test.snap");
File.WriteAllText(filename, data);
Just in case, add the line below too (before File.WriteAllText); perhaps your file already exists as, I don't know, read-only:
File.SetAttributes(filename, FileAttributes.Temporary);

How can I read from a network path on my server using asp.net mvc application

I have setup a network path on the web server running the .net MVC application to another server that is being used for storage of files that are uploaded. In order to allow for uploading files to the network path through the application I amended the application pool in IIS so that the application had permission to upload to it. This all works fine.
In order for the app to read the file when loading a page I created a virtual directory in IIS which appears now as part of the file system for that site in IIS. How can I then access and generate a path for this location in my app so that the path can be passed to a DB and then the image loaded later on a page?
Let's say my path is:
virtual path is useruploads/image.jpg
How can I have my application recognise the virtual path I've created?
Alternatively scrapping the virtual directory in IIS, is there a way I can have my application access the network path on the web server directly?
edit - To add to the above I have the network path mapped in iis and it shows under my site in iis as follows:
I would like to then read from this directory inside the asp.net mvc app using an address like this ~/useruploads/file.jpg. If I can get my app to recognise this virtual directory then I will be able to read from it.
edit no.2
Ok I think I have determined the nature of my issue and am a lot clearer. Basically I am accessing the file just fine when I link to it on the webpage. However I get the message 'not allowed to load local resource' in chrome. I believe this is simply a question of web browsers not allowing me to load the image because it is referenced on the page with a local address (the network path). As such I am now trying to create an action in the application to load the image with Url.Action directed at my action which will then convert the id into a stream of the image e.g.
In my view:
<img src="#Url.Action("image", "files", new { image = Model.image})" alt="name" />
Where Mode.image is something like folder\image.jpg.
Action:
public FileResult image(string id)
{
var dir = #"\\SERVERNAME\upload\";
var path = Path.Combine(dir, id);
return base.File(path, "image/jpg");
}
So in the end my issue was that I was unable to place the image on the page as it was using a local address on the server which is not allowed for security reasons in browsers. In order to use the network path I had to use
<img src="#Url.Action("image", "files", new { image = Model.image})" alt="name" />
in my view to this action:
public FileResult image(string id)
{
var dir = #"\\SERVERNAME\upload\";
var path = Path.Combine(dir, id);
string contentType = MimeMapping.GetMimeMapping(path);
return base.File(path, contentType);
}
This then returns the image to the page although it means the image does not get cached.

UWP - Get path to user download folder

I have been looking for a little while now and am not finding much help via MSDN resources and others.
My predicament is simple: my app needs a base directory to the Downloads folder. I am aware of the DownloadsFolder class however that is not suiting my needs currently.
How do I get the current user's Download folder path in a Windows Universal App?
Use Windows.Storage.UserDataPaths to get the path of user's download folder.
string downloadsPath = UserDataPaths.GetDefault().Downloads;
This method is introduced in build 16232, so clients with RS3(1709) or later will be able to run it.
You shouldn't obtain downloads folder path using LocalFolder, which might result in wrong folder when the user changed the default location for it.
System.Environment.ExpandEnvironmentVariables("%userprofile%/downloads/")
Is that what you need?
string localfolder = ApplicationData.Current.LocalFolder.Path;
var array = localfolder.Split('\\');
var username = array[2];
string downloads = #"C:\Users\" + username + #"\Downloads";
This will result
C:\Users\username\Downloads
The DownloadsFolder for an app now defaults to a folder withing the user's Downloads directory named after the app name (in actual fact the app name folder is simply a link to a folder named after the Package Family Name)
To get the folder name, I used the following hack (vb) to first create a dummy file in the UWP app's DownloadsFolder then using .NET code to get the directory name, and finally deleting the dummy file.
Dim o As StorageFile = Await DownloadsFolder.CreateFileAsync("dummy.txt", CreationCollisionOption.GenerateUniqueName)
Dim dirName Ss String = Path.GetDirectoryName(o.Path)
Await o.DeleteAsync(StorageDeleteOption.PermanentDelete)

HttpPostedFile.SaveAs access denied

I'm trying to save a file that I upload from my page but I'm getting an access denied error:
public void SaveFile(Item item, HttpPostedFileBase file)
{
var dir = string.Format(#"{0}\NSN\{1}", ConfigurationManager.AppSettings["ContentLocation"].ToString(), item.Id.ToString());
if (!System.IO.Directory.Exists(dir))
System.IO.Directory.CreateDirectory(dir);
Array.ForEach(Directory.GetFiles(dir), File.Delete);
file.SaveAs(dir);
}
I'm running this site from the local host from visual studio so no app pool is involved. I've given the Network Service (and Everyone as a test) full control of the folder and it's subfolders. Strange thing is it creates the folder if it needs to and also deletes any files in an existing folder. Only when I call the SaveAs function do I get the error.
You call file.SaveAs with path to directory instead of path to file
Here, give this a try:
string saveAsPath = Path.Combine(dir, file);
file.SaveAs(saveAsPath);
Replace file.SaveAs(dir) with the above.

Providing links to files outside of IIS

I have an upload function in my system that stores the files on the d drive e.g D:\KBFiles. Now i need to offer these files as links through internet explorer. Obviously i cant just offer a path e.g D:\KBFiles\test.pdf. Whats the best way to handle this scenario
Write "proxy" file with such code and call it DownloadFile.aspx:
string fileName = Request.QueryString["file"];
string filePath = Path.Combime("D:\\KBFile", fileName);
Response.WriteFile(filePath);
Then have such link:
test.pdf
This allows you to check the user permissions if you're using Login system and also you can check the requested file against some white list to prevent hacking attempts.
You need to create a Virtual Folder for that windows folder inside your WebApplication. As soon as IIS has mapped the virtual folder, it would be possible to use direct links, which would have your WebApplication as a root.

Categories