Access to path denied for files in WWWRoot folder - c#

I am trying to access two folders that resides inside wwwRoot. The folders are "BlankPDFFiles" and "FilledPDFFiles". I am trying to get the blank PDF files that resides inside BlankPDFFiles folder and write some data inside the file and then save it to the folder FilledPDFFiles. This is the structure of my solution:
when I am trying to access the blank PDF file, I am getting below error:
Below is my code
public class PDFController : Controller
{
private readonly IEmployeeService _employeeService;
public readonly IConfiguration _configuration;
public readonly ILogger _logger;
private readonly IWebHostEnvironment _environment;
public PDFController(IEmployeeService employeeService, IConfiguration configuration, ILogger<PDFController> logger, IWebHostEnvironment environment)
{
_employeeService = employeeService;
_configuration = configuration;
_logger = logger;
_environment = environment;
}
public async Task<IActionResult> Index()
{
await PopulatePDFDoc();
return View();
}
public async Task PopulatePDFDoc()
{
AckPackage.Data.PDFPopulate.DocPDF doc = new Data.PDFPopulate.DocPDF();
string pdfLic = _configuration["PDFLicense"].ToString();
string filledPDF = Path.Combine(_environment.WebRootPath, "FilledPDFFiles");
string blankPDF = Path.Combine(_environment.WebRootPath, "BlankPDFFiles");
EmployeeInfo employee = await _employeeService.GetEmployeeByEmployeeNumber(up.EmployeeId);
await doc.popolatePDFDoc(pdfLic, filledPDF, blankPDF, employee);
}
This is what I have in populatePDFDoc method:
public async Task popolatePDFDoc(string PDFLic, string filledPDF, string blankPDF, EmployeeInfo employee)
{
string pathToFile = filledPDF + "_Package"+ "_" + employee.EmployeeNumber;
bool validLicense = BitMiracle.Docotic.LicenseManager.HasValidLicense;
**using (PdfDocument pdf = new PdfDocument(blankPDF))**
{
foreach (PdfControl control in pdf.GetControls())
{
switch (control.Name)
{
case "EmpID":
((PdfTextBox)control).Text = employee.EmployeeNumber;
break;
case "Last Name":
((PdfTextBox)control).Text = employee.LastName;
break;
}
}
pdf.Save(pathToFile);
}
I am getting an error at this line in popolatePDFDoc
using (PdfDocument pdf = new PdfDocument(blankPDF))
I am using third party vendor tool to populate PDF file.

This has nothing to do with the PDF library vendor, and is probably because your web application's exe is running from a directory outside of the path that blankPDF points to.
You can see the directory your application is running from by putting a call to Environment.CurrentDirectory somewhere before the exception occurs and putting a breakpoint on it to see it.
If you are developing locally and getting this error, your application is probably running in the folder C:\AllITProjects\AckPackage\bin\Debug\net7.0.
The wwwroot folder is not always writable from the application, depending on how you are running the application. However, the project folder is always writable, since the project folder is where your application's exe resides. So, here are steps to move your PDF folders into the project folder, which will ensure they remain writable:
1. Move both the BlankPDFFiles and FilledPDFFiles folders out of the wwwroot folder, and into the project folder (the folder where your .csproj file is located).
2. Open the .csproj file in notepad, and add the following line that tells the compiler to copy your PDF folders whenever you compile:
<ItemGroup>
<None Include="BlankPDFFiles\**" CopyToOutputDirectory="Always" CopyToPublishDirectory="Always" />
<None Include="FilledPDFFiles\**" CopyToOutputDirectory="Always" CopyToPublishDirectory="Always" />
</ItemGroup>
3. Change filledPDF and blankPDF to use _environment.ContentRootPath:
string filledPDF = Path.Combine(_environment.ContentRootPath, "FilledPDFFiles");
string blankPDF = Path.Combine(_environment.ContentRootPath, "BlankPDFFiles");
4. You will have to create an action in a controller that specifically serves the PDF from the filledPDF path, as it will no longer reside in the wwwroot folder.
Let me know if you need more help, especially with Step #4.

Related

Reading files in a folder one by one in C#

I have a simple question, but that it's driving me crazy.
I have a folder with a lot of JSON files inside, I need just to open those files one by one and do things.
So, I need to open the first one, read it and do something then go to the second one etc. until the last.
Here the code I tried, searching on web:
string folderpath = #"C:\Users\rfo\Desktop\MM\VM DB\nv - master\nvd";
var fixedfolderpath = Environment.ExpandEnvironmentVariables(folderpath);
string [] filesnumber = Directory.GetFiles(fixedfolderpath, "*.json");
foreach (string filename in filesnumber)
{
var jsonFull = System.IO.File.ReadAllText(filename);
But I keep gettin error DirectoryNotFoundException: Could not find a part of the path on the browser.
I'm using asp.net CORE 3 and visual studio 2019.
If you want to get the files from your desktop, you could use this instead:
string path = Path.Combine(
Environment.GetFolderPath(Environment.SpecialFolder.Desktop),
#"MM\VM DB\nv - master\nvd");
But you probably got access right problems
As others suggest, it's better to move the files inside the solution. For example, you could copy those files into the project folder:
├───YourApp.csproj # your `*.csproj`
├───Controllers/
├───Models/
├───Views/
├───wwwroot/
├───MM/ # your target folder
│ └───VM/
│ └───VM DB/
│ └───nv - master/
│ └───nvd/
| └─── *.json
Apart from that, don't forget to add a configuration in your *.csproj file such that your json files will be copied automatically when you build/publish the app:
<ItemGroup>
<None Update="MM\**\*.*">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
</ItemGroup>
Also, it seems that you're using the ASP.NET Core instead of ASP.NET. If that's the case, you could reference this folder by injecting an IWebHostEnvironment so that you invoke webHostEnv.ContentRootPath to get root directory (i.e., the project file direction in source code, and the directory of YourAssembly.dll when published).
For example, you could inject an IWebHostEnviroment instance in the controller:
public class HomeController : Controller
{
private readonly IWebHostEnvironment _env;
public HomeController(IWebHostEnvironment env)
{
this._env = env;
}
public IActionResult Privacy()
{
var dir = Path.Combine( this._env.ContentRootPath, #"MM\VM\VM DB\nv - master\nvd");
string[] filesnumber = Directory.GetFiles(dir, "*.json");
foreach (string filename in filesnumber)
{
var jsonFull = System.IO.File.ReadAllText(filename);
...
}
return Ok("hello,world");
}
}
Finally, in case you don't like dependency injection, or cannot use dependency injection, you could use a path relative to current assembly as below:
var assemDir = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);
var dir = Path.Combine(assemDir, #"MM\VM\VM DB\nv - master\nvd");
string[] filesnumber = Directory.GetFiles(dir, "*.json");
Be careful this is not the safest way. It is suggested that we should use the DI approach (IWebHostEnviroment) if possible.

Using log4net With Visual Studio Debugger

I'm having trouble getting my log4net.config file to load when using Visual Studio in debug mode for an Excel VSTO Plugin. The config file is in the top level directory of my project. I have the property "Copy to Output Directory" set to "Copy Always". This ensures the file is copied to bin/Debug/log4net.config. I can verify this is the case when I build.
However, the file won't load when I run in Debug mode. I gave up on trying to get the file to load automatically and decided to do it by code, as per the OP's code at the bottom of this question.
However, I realised that I needed to use an absolute path to the config file, as relative paths weren't picking it up. On further investigation, I realised that the executing DLL wasn't actually the DLL in the debug/bin folder. It was in the following location:
C:\Users\cbhandal\AppData\Local\assembly\dl3\MO52QQWP.9ZL\K36XZHGN.1PB\230751e6\d09b7fb2_19f6d401
Also the current working directory, as found by System.IO.Directory.GetCurrentDirectory(); was set to "C:\\Users\\cbhandal\\Documents".
Hard-coding the path as an absolute path works as in the following code:
var log4netConfig = "C:\\" + path + "\\Log4net.config";
var log4netInfo = new FileInfo(log4netConfig);
log4net.Config.XmlConfigurator.ConfigureAndWatch(log4netInfo);
But that's not a solution I can deploy. I'm stuck here. Wondering if there's a way to either force Visual studio to copy the .config file to that appdata/temp location, or if there's a way to programatically reference the folder where the original DLL lay- the one that was built. Or if anyone had any other solution?
For me the easiest solution was to use this:
https://stackoverflow.com/a/6963420/4754981
But there are several other solutions on that link for different approaches, each with their caveats.
So mine looks like this:
using System.Reflection;
using System.IO;
using System;
public static class Extensions {
private static string GetDirectory(this Assembly a) {
string codeBase = a.CodeBase;
UriBuilder uri = new UriBuilder(codeBase);
string path = Uri.UnescapeDataString(uri.Path);
return Path.GetDirectoryName(path);
}
private static void AlterLogPath(this log4net.Repository.ILoggerRepository repo, string newPath, string directory="") {
log4net.Repository.Hierarchy.Hierarchy h = (log4net.Repository.Hierarchy.Hierarchy) repo;
foreach (log4net.Appender.IAppender a in h.Root.Appenders) {
if (a is log4net.Appender.FileAppender) {
var fa = (log4net.Appender.FileAppender)a;
var fileName = Path.GetFileName(fa.File);
fa.File = newPath + (String.IsNullOrEmpty(directory)?"":(directory + Path.DirectorySeparatorChar.ToString())); // edit: filename is attached after next line automatically.
fa.ActivateOptions();
break;
}
}
}
}
and in the bootup (via [assembly: System.Web.PreApplicationStartMethod] or otherwise for asp), or main app..
static void Main() {
var PATH = Assembly.GetExecutingAssembly().GetDirectory() + Path.DirectorySeparatorChar.ToString();
log4net.Config.XmlConfigurator.ConfigureAndWatch(new FileInfo(PATH + "log4net.config"));
log4net.LogManager.GetRepository().AlterLogPath(PATH, "Logs");
}

How do I read in a file from disc during runtime in my WebAPI .NET Core [duplicate]

This question already has answers here:
How do I read a local file in my ASP.NET Core project?
(2 answers)
Closed 4 years ago.
In my solution I have a file called beep.png directly in the root, right next to Startup.cs file. I changed its properties to always copy. I activated UseFileServer and opted in to browse the directory structure to be sure.
However, when I run the code Image.FromFile("beep.png");, I only get the error that the file isn't found.
System.IO.FileNotFoundException
Message=C:\Program Files\IIS Express\beep.png
How can I enable the file to be accessible?
Use IWebHostEnvirounment to get the root content path (technicaly the project folder), or to get the web root path (the wwwroot folder under project folder).
_hostingEnvirounment.ContentRootPath will return:
D:\Hosting\ProjectFolder
_hostingEnvirounment.WebRootPath will rturn:
D:\Hosting\ProjectFolder\wwwroot
So in your case; inject IWebHostEnvirounment to your controller then get the content root folder as below:
public class MyApiController : ControllerBase {
private readonly IWebHostEnvirounment _hostingEnvirounment;
public MyApiController(IWebHostEnvironment hostingEnvironment)
{
_hostingEnvironment = hostingEnvironment;
}
// get image from project root folder \ProjectFolder\
public Image GetImageFromContentRoot(string name) {
// e.g.: imgPath = "D:\\Hosting\\ProjectFolder\\beep.png"
var imgPath = Path.Combine(_hostingEnvirounment.ContentRootPath, name);
return Image.FromFile(imgPath);
}
//get image from projects wwwroot folder
public Image GetImageFromWebRoot(string name) {
// e.g.: imgPath = "D:\\Hosting\\ProjectFolder\\wwwroot\\beep.png"
var imgPath = Path.Combine(_hostingEnvirounment.WebRootPath, name);
return Image.FromFile(imgPath);
}
}

Folder of running application

I have problem with my C# app, when is opened via file association, it works in file directory. For example, when I create copy of opened file:
File.Copy("C:\Photo\car.jpg", ".\car2.jpg"); // this is only ilustration code.
It makes new file "C:\Photo\car2.jpg", but I want to make file in my app directory (".\car2.jpg").
So, I think, when app is opened via file association, it run with working folder of that file ("C:\Photo\"). Is there way, how to keep working directory as directory with app.exe?
Edit:
This is not solution, I need to get equals of ".\" and System.AppDomain.CurrentDomain.BaseDirectory:
File.Copy("C:\Photo\car.jpg", Path.Combine(System.AppDomain.CurrentDomain.BaseDirectory, "car2.jpg"));
I have to use this on many places in application, solution can be sets:
Environment.CurrentDirectory = System.AppDomain.CurrentDomain.BaseDirectory;
but I prefer set it in startup application via file association and not in running program - it looks cleaner.
Thanks,
Jakub
To get the path of your application, you can use:
System.AppDomain.CurrentDomain.BaseDirectory
Use Path.Combine to build the destination path as follows:
File.Copy("C:\Photo\car.jpg", Path.Combine(System.AppDomain.CurrentDomain.BaseDirectory, "car2.jpg"));
I'd like to try and offer an alternative. I'm relatively new to this, but I figured out a solution that works for me:
Make 2 static variables in your MainWindow.xaml.cs:
public static string fileOpen;
public static string workingDirectory;
In your app.xaml.cs file, add the following code:
protected override void OnStartup(StartupEventArgs e)
{
if (e.Args.Count() > 0)
{
var a = File.Exists(e.Args[0]);
var path = Path.GetFullPath(e.Args[0]);
MainICPUI.workingDirectory = Directory.GetCurrentDirectory();
MainICPUI.fileOpen = e.Args[0];
}
base.OnStartup(e);
}
When you open a file associated with your program from any directory, the full file name is added to the StartupEventArgs, including the directory. This code saves the directory.
Back to your MainWindow.xaml.cs file:
public static string fileOpen;
public static string workingDirectory;
public MainWindow()
{
InitializeComponent();
Directory.SetCurrentDirectory(AppDomain.CurrentDomain.BaseDirectory);
// This sets the directory back to the program directory, so you can do what you need
// to with files associated with your program
}
// Make sure your MainWindow has an OnLoaded event assigned to:
private void MainWindow_OnLoaded(object sender, RoutedEventArgs e)
{
// Now I'm setting the working directory back to the file location
Directory.SetCurrentDirectory(workingDirectory);
if (File.Exists(fileOpen))
{
var path = Path.GetFullPath(fileOpen);
// This should be the full file path of the file you clicked on.
}
}

Why AppDomain.CurrentDomain.BaseDirectory not contains "bin" in asp.net app?

I have a web project like:
namespace Web
{
public partial class _Default : System.Web.UI.Page
{
protected void Page_Load(object sender, EventArgs e)
{
lbResult.Text = PathTest.GetBasePath();
}
}
}
The method PathTest.GetBasePath() is defined in another Project like:
namespace TestProject
{
public class PathTest
{
public static string GetBasePath()
{
return AppDomain.CurrentDomain.BaseDirectory;
}
}
}
Why it's display ...\Web\ while the TestProject assembly is compiled into bin folder(in other words it should display ...\Web\bin in my thought).
Now I got a troublesome if I modified method into:
namespace TestProject
{
public class FileReader
{
private const string m_filePath = #"\File.config";
public static string Read()
{
FileStream fs = null;
fs = new FileStream(AppDomain.CurrentDomain.BaseDirectory + m_filePath,FileMode.Open, FileAccess.Read);
StreamReader reader = new StreamReader(fs);
return reader.ReadToEnd();
}
}
}
The File.config is created in TestProject. Now AppDomain.CurrentDomain.BaseDirectory + m_filePath will returen ..\Web\File.config (actually the file was be copied into ..\Web\bin\File.config), an exception will be thrown.
You could say that I should modified m_filePath to #"\bin\File.config". However If I use this method in a Console app in your suggest, AppDomain.CurrentDomain.BaseDirectory + m_filePath will return ..\Console\bin\Debug\bin\File.config (actually the file was copyed into .\Console\bin\Debug\File.config), an exception will be thrown due to surplus bin.
In other words, in web app, AppDomain.CurrentDomain.BaseDirectory is a different path where file be copyed into (lack of /bin), but in console app it's the same one path.
Any one can help me?
Per MSDN, an App Domain "Represents an application domain, which is an isolated environment where applications execute." When you think about an ASP.Net application the root where the app resides is not the bin folder. It is totally possible, and in some cases reasonable, to have no files in your bin folder, and possibly no bin folder at all. Since AppDomain.CurrentDomain refers to the same object regardless of whether you call the code from code behind or from a dll in the bin folder you will end up with the root path to the web site.
When I've written code designed to run under both asp.net and windows apps usually I create a property that looks something like this:
public static string GetBasePath()
{
if(System.Web.HttpContext.Current == null) return AppDomain.CurrentDomain.BaseDirectory;
else return Path.Combine(AppDomain.CurrentDomain.BaseDirectory,"bin");
}
Another (untested) option would be to use:
public static string GetBasePath()
{
return System.Reflection.Assembly.GetExecutingAssembly().Location;
}
In case you want a solution that works for WinForms and Web Apps:
public string ApplicationPath
{
get
{
if (String.IsNullOrEmpty(AppDomain.CurrentDomain.RelativeSearchPath))
{
//exe folder for WinForms, Consoles, Windows Services
return AppDomain.CurrentDomain.BaseDirectory;
}
else
{
//bin folder for Web Apps
return AppDomain.CurrentDomain.RelativeSearchPath;
}
}
}
The above code snippet is for binaries locations.
The AppDomain.CurrentDomain.BaseDirectory is still a valid path for Web Apps, it's just the root folder where the web.config and Global.asax are, and is same as Server.MapPath(#"~\");
If you use AppDomain.CurrentDomain.SetupInformation.PrivateBinPath instead of BaseDirectory, then you should get the correct path.
When ASP.net builds your site it outputs build assemblies in its special place for them. So getting path in that way is strange.
For asp.net hosted applications you can use:
string path = HttpContext.Current.Server.MapPath("~/App_Data/somedata.xml");

Categories