DirectoryNotFoundException when trying to save image to wwwroot - c#

using this question I came up with this code to save a file
[HttpPost]
public IActionResult Upload(string office, IFormFile file)
{
if (file.Length > 0) {
var filePath = Path.GetFullPath(Path.Combine(_environment.WebRootPath,
_configuration["SydneyFloorplanPath"]));
using (var fileStream = new FileStream(filePath, FileMode.Create)) {
file.CopyTo(fileStream);
}
}
return RedirectToAction("Index", new{office = office});
}
But I get a super annoying issue when instantiating FileStream:
An exception of type 'System.IO.DirectoryNotFoundException' occurred in System.Private.CoreLib.dll but was not handled in user code: 'Could not find a part of the path 'C:\images\Floorplan\sydney.pdf'.'
But That's not the path that I need. The Images folder sits in my project under wwwroot\images...
what is the point of WebRootPath if it doesn't actually give me a usable, relative path?
Note that _environment is an injected IHostingEnvironment
I also noticed that if I call
var two = _environment.WebRootPath;
the varaible contains the full path... "C:\\Users\\bassie\\source\\repos\\TFS\\DSSTools\\wwwroot"
But after calling Path.Combine, I suddenly have "C:\\images\\Floorplan\\sydney.pdf" ... why?
Edit:
_environment is a IHostingEnvironment, eg:
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Mvc;
namespace AspNetCorePathMapping
{
public class HomeController : Controller
{
private readonly IHostingEnvironment _hostingEnvironment;
public HomeController(IHostingEnvironment hostingEnvironment)
{
_hostingEnvironment = hostingEnvironment;
}
public ActionResult Index()
{
string webRootPath = _hostingEnvironment.WebRootPath;
string contentRootPath = _hostingEnvironment.ContentRootPath;
return Content(webRootPath + "\n" + contentRootPath);
}
}
}

I managed to get this working with
[HttpPost]
public IActionResult Upload(string office, IFormFile file)
{
var webRootPath = _environment.WebRootPath;
var floorPlanPath = _configuration["SydneyFloorplanPath"];
if (file.Length > 0) {
var filePath1 = Path.Combine(floorPlanPath,webRootPath.ReplaceFirst("/", ""));
using (var fileStream = new FileStream(filePath1, FileMode.Create)) {
file.CopyTo(fileStream);
}
}
return RedirectToAction("Index", new{office = office});
}
Where ReplaceFirst is a simple StringExtension
public static string ReplaceFirst(this string text, string search, string replace)
{
int pos = text.IndexOf(search);
if (pos < 0)
{
return text;
}
return $"{text.Substring(0, pos)}{replace}{text.Substring(pos + search.Length)}";
}
So it seems the leading / in webRootPath was breaking my Path.Combine call

Related

The request matched multiple endpoints. Matches:

i am using asp.net core 3.1 this code in used for saving data into database and iIt works fine until i add the code for search it gives this error (The request matched multiple endpoints. Matches:)
this is the controller
namespace Info.Controllers
{
public class DemoController : Controller
{
private readonly ApplicationDbContext _context;
public DemoController(ApplicationDbContext context)
{
_context = context;
}
public IActionResult Index()
{
var result = _context.Files.ToList();
return View(result);
}
[HttpPost]
public IActionResult Index(IFormFile files)
{
if (files != null)
{
if (files.Length > 0)
{
//Getting FileName
var fileName = Path.GetFileName(files.FileName);
//Getting file Extension
var fileExtension = Path.GetExtension(fileName);
// concatenating FileName + FileExtension
//var newFileName = String.Concat(Convert.ToString(Guid.NewGuid()), fileExtension);
var newFileName = String.Concat((Guid.NewGuid()), fileExtension);
var objfiles = new Files()
{
DocumentId = 0,
Name = newFileName,
FileType = fileExtension,
};
using (var target = new MemoryStream())
{
files.CopyTo(target);
objfiles.DataFiles = target.ToArray();
}
_context.Files.Add(objfiles);
_context.SaveChanges();
}
}
return RedirectToAction("Index");
}
public async Task<IActionResult> DownloadImage(string filename,int id)
{
if (filename == null)
return Content("filename is not availble");
var file = await _context.Files.Where(x => x.DocumentId == id).FirstOrDefaultAsync();
var path = Path.Combine(Directory.GetCurrentDirectory(), filename);
var memory = new MemoryStream();
{
}
memory.Position = 0;
return File(memory, GetContentType(path), Path.GetFileName(path));
}
private string GetContentType(string path)
{
var types = GetMimeTypes();
var ext = Path.GetExtension(path).ToLowerInvariant();
return types[ext];
}
private Dictionary<string, string> GetMimeTypes()
{
return new Dictionary<string, string>
{
{".txt", "text/plain"},
{".pdf", "application/pdf"},
{".doc", "application/vnd.ms-word"},
{".docx", "application/vnd.ms-word"},
{".xls", "application/vnd.ms-excel"},
{".xlsx", "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"},
{".png", "image/png"},
{".jpg", "image/jpeg"},
{".jpeg", "image/jpeg"},
{".gif", "image/gif"},
{".csv", "text/csv"}
};
}
public IActionResult Privacy()
{
return View();
}
[HttpPost]
public async Task<IActionResult> Index(string searchString)
{
var FileController = from m in _context.Files
select m;
if (!String.IsNullOrEmpty(searchString))
{
FileController = FileController.Where(s => s.Name!.Contains(searchString));
}
return View(await FileController.ToListAsync());
}
}
}
I've tried modifying the code for search but to no avail
Shouldn't your search be a HttpGet? if both functions are HttpPost how will it know which one to call?
I am using asp.net core 3.1 this code in used for saving data into
database and iIt works fine until i add the code for search it gives
this error (The request matched multiple endpoints. Matches:)
Well, your exceprtion is pretty obvious. Because in asp.net core regardless of its version deosn't accept same action name with same HTTP VERB consequently you are getting the exception. You have following action ambiguity
public IActionResult Index()
[HttpPost]
public IActionResult Index(IFormFile files)
//Should Rename To Create
[HttpPost]
public async Task<IActionResult> Index(string searchString)
//Should Rename to Search
I am hoping you got following error:
Exception Reproduced:
Solution:
It can be resolved in many ways, for instance, you can rename your controller action, you can modify the HTTP Verb. If you just rename your public async Task<IActionResult> Index(string searchString) to Search instead of Index or modify the http verb from [HttpPost] to [HttpGet] your issue would be resolved.
Output:
Note: You should always keep in mind while define a name for your controller, action, method, or variable for the sake of readability.
* Index - the main "landing" page. This is also the default endpoint.
* Edit - an edit page for the "thing"
* New - a create page for the "thing"
* Create - creates a new "thing" (and saves it if you're using a DB)
* Search- While searching from list or anything.
You can have look on naming convensions guidelines for asp.net core here

ASP.NET CORE 5 MVC How Can I Convert Absolut Path To Relative Path?

so in the project a user could upload, download, delete, preview and search for (in real time) a file. The files are stored in a folder called "Uploads" inside of the wwwroot folder. As of now, the code has absolute pathing, but the project will need to be on different computers so I need relative pathing, can anyone help me out with a sample syntax?
[HttpPost]
public async Task<IActionResult> Index(IFormFile fifile, string category)
{
string path = #"C:\Users\panara5\source\repos\Proiect2PracticaClona\Proiect2PracticaClona\wwwroot\Uploads\";
var fisave = Path.Combine(path, fifile.FileName);
var stream = new FileStream(fisave, FileMode.Create);
await fifile.CopyToAsync(stream);
Files fileModel=new Files()
{
info = fifile.FileName,
category = category
};
_fileRepository.AddFile(fileModel);
stream.Close();
files = _fileRepository.GetFileList();
return RedirectToAction("Index",files);
}
public IActionResult Index(string id)
{
files = _fileRepository.GetFileList();
if (id == null)
return View(files);
List<Files> sortedfiles = new List<Files>();
foreach (var item in files)
{
if (item.category == id)
{
sortedfiles.Add(item);
}
}
return View(sortedfiles);
}
public IActionResult Delete(string filedel)
{
filedel = Path.Combine("~/Uploads/", filedel);
FileInfo fi = new FileInfo(filedel);
if (fi != null)
{
System.IO.File.Delete(filedel);
fi.Delete();
}
return RedirectToAction("Index");
}
also i get a null reference when trying to delete
I believe you want IWebHostEnvironment.WebRootPath
You need to use Dependency Injection to get access to it, but after that, you can just reference and Path.Combine. See below:
public class FileController : Controller
{
private readonly IWebHostEnvironment environment;
public FileController(IWebHostEnvironment env)
{
this.environment = env;
}
private string UploadFilePath() => Path.Combine(environment.WebRootPath, "uploads");
}

Why doesn't this ASP.NET Core code work on remote host

I have an Angular web site that works on my local host. When I upload it to a remote host, it fails without an error. Just doesn't save any files.
I have searched the web for an answer. No joy.
This never returns an error on either the local Visual Studio 2019 or the remote.
Found the basic code on Microsoft site. Looked at a lot of angular sites
[Route("api/[controller]")]
[ApiController]
public class UploadController : ControllerBase
{
private IHostingEnvironment env;
public UploadController(IHostingEnvironment hostingEnvironment)
{
env = hostingEnvironment;
}
[HttpPost, DisableRequestSizeLimit]
public async Task<IActionResult> Upload()
{
try
{
var files = Request.Form.Files;
if (files.Count < 1)
{
return BadRequest();
}
if (files.Any(f => f.Length == 0))
{
return BadRequest();
}
foreach (var file in files)
{
string folderName = $"Project_{file.FileName}";
string webRootPath = env.WebRootPath;
string newPath = Path.Combine(Startup.documentPath, folderName);
if (!Directory.Exists(newPath))
{
Directory.CreateDirectory(newPath);
}
if (file.Length > 0)
{
string fileName = ContentDispositionHeaderValue.Parse(file.ContentDisposition).Name.Trim('"');
string fullPath = Path.Combine(newPath, fileName);
if (System.IO.File.Exists(fullPath))
{
System.IO.File.Delete(fullPath);
}
using (var stream = new FileStream(fullPath, FileMode.Create))
{
await file.CopyToAsync(stream);
}
}
}
return Ok("All the files are successfully uploaded.".csConverToJSON());
}
catch (Exception ex)
{
return StatusCode(500, ex.Message);
}
}
}

Asp.net core I could add pictures, but I couldn't delete the image

I'm using Asp.Net MVC Core. I could add pictures, but I couldn't delete the image. I could not use the following methods: Server, MapPath.
Entity add and Image upload:(successful method.I just shared with you about how I added it.)
public async Task<IActionResult> Create(IFormFile image,Programci programci)
{
if (image==null || image.Length==0)
{
return Content("not image selected");
}
var path = Path.Combine(Directory.GetCurrentDirectory(), "wwwroot/image/Programcilar", image.FileName);
using (var stream = new FileStream(path, FileMode.Create))
{
await image.CopyToAsync(stream);
}
programci.ImageUrl = "/image/Programcilar/"+ image.FileName;
_programciService.Add(programci);
return RedirectToAction("Index");
}
Delete Method:(Problem.I couldn't delete the image.)
public ActionResult Delete(int id)
{
var bulunanProgramci = _programciService.Get(id);
_programciService.Delete(id);
return RedirectToAction("Index");
}
Based on your code and the images you should have something like this in the code that is inside _programciService.Delete(id); method call
var path = Path.Combine(Directory.GetCurrentDirectory(), "wwwroot\\image\\Programcilar", "controlller.jpg");
if(System.IO.File.Exists(path))
{
System.IO.File.Delete(path);
}
Just replace static image file name "controlller.jpg" with the variable name of the image you are trying to delete.
Be careful with the path of the image. It should be the full path from the root drive. For example C:\\inetpub\\wwwroot\\image\\Programcilar\\controlller.jpg.
I suggest to use an environmental variable to store/retrieve the path where you have stored your image.
string uploadsFolder = Path.Combine(webHostEnvironment.WebRootPath, "Images");
var path = System.IO.Path.Combine(Directory.GetCurrentDirectory(), uploadsFolder, picName);
if (System.IO.File.Exists(path))
{
System.IO.File.Delete(path);
}
Don't forget to use:
private readonly IWebHostEnvironment webHostEnvironment;
and to instatiate it in the constructor of your class:
public yourClass(yourContext context, IWebHostEnvironment hostEnvironment)
{
_context = context;
webHostEnvironment = hostEnvironment;
}

asp.net no response from post request after injecting HostingEnvironment

I only have basic knowledge about ASP.net. I have been writing a simple MVP controller, that processes a JSON post request. It worked fine until I tried injecting Hosting environment. Since then the server always responds with code 500, but there are no errors on the server itself. When setting breakpoints in the controller it seems like the post code is never reached, not even the constructor is called after the injection. I have no idea where to look for the reason for this. I hope someone can help me here. Here is the controller code:
using System;
using System.Net;
using System.Net.Http;
using System.IO;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNet.Authorization;
using Microsoft.AspNet.Mvc;
using PdfSharp;
using PdfSharp.Pdf;
using PdfSharp.Pdf.Annotations;
using PdfSharp.Pdf.Advanced;
using PdfSharp.Pdf.AcroForms;
using PdfSharp.Pdf.IO;
using st214.Models;
using Microsoft.AspNet.Hosting;
// For more information on enabling Web API for empty projects, visit http://go.microsoft.com/fwlink/?LinkID=397860
namespace st214.API
{
[Route("api/[controller]")]
public class FormularController : Controller
{
private readonly ApplicationDbContext db;
private readonly HostingEnvironment _hostingEnvironment;
private readonly string tempLocation;
private Dictionary<string, string> locationsMock = new Dictionary<string, string>();
public FormularController(ApplicationDbContext context, HostingEnvironment hostingEnvironment)
{
db = context;
_hostingEnvironment = hostingEnvironment;
tempLocation = _hostingEnvironment.WebRootPath + #"\app\formularwesen\tempForms\";
locationsMock.Add(#"ct", #"CT_form.pdf");
locationsMock.Add(#"uewa", #"UeWA_form.pdf");
locationsMock.Add(#"uelab", #"UeWA_form.pdf");
locationsMock.Add(#"uelabalt", #"UeWA_form.pdf");
}
// GET: api/values
[HttpGet]
public IEnumerable<string> Get()
{
return new string[] { "value1", "value2" };
}
// GET api/values/5
[HttpGet("{id}")]
public string Get(int id)
{
return "value";
}
// POST api/values
[HttpPost]
public FileStreamResult Post([FromBody] FormData formData)
{
var source = _hostingEnvironment.WebRootPath + #"\app\formularwesen\PDFForms\" + locationsMock[formData.name];
var file = FillForm(source, tempLocation, formData.data, formData.print);
FileStream pdfStream = new FileStream(file, FileMode.Open, FileAccess.Read);
return new FileStreamResult(pdfStream, "application/pdf")
{
FileDownloadName = formData.name + ".pdf"
};
}
// PUT api/values/5
[HttpPut("{id}")]
public void Put(int id, [FromBody] FormData formData)
{
}
// DELETE api/values/5
[HttpDelete("{id}")]
public void Delete(int id)
{
}
private string FillForm(string source, string destinationFolder, Dictionary<string, FormField> fields, bool print)
{
// Open document
PdfDocument document = PdfReader.Open(source, PdfDocumentOpenMode.Modify);
// set fields to be editable
if (document.AcroForm.Elements.ContainsKey("/NeedAppearances") == false)
document.AcroForm.Elements.Add("/NeedAppearances", new PdfSharp.Pdf.PdfBoolean(true));
else
document.AcroForm.Elements["/NeedAppearances"] = new PdfSharp.Pdf.PdfBoolean(true);
// fill out fields
foreach (KeyValuePair<string, FormField> field in fields)
{
// get the field
if (field.Value.Type == "text")
{
PdfTextField currentField = (PdfTextField)(document.AcroForm.Fields[field.Key]);
// create the value
PdfString valueString = new PdfString(field.Value.Value);
// fill the value
try
{
currentField.Value = valueString;
}
catch (NullReferenceException e)
{
// Field not found
}
}
if (field.Value.Type == "radio")
{
PdfCheckBoxField currentField = (PdfCheckBoxField)(document.AcroForm.Fields[field.Value.Value]);
try
{
if (currentField.HasKids)
{
foreach (var item in currentField.Fields.Elements.Items)
{
//assumes you want to "check" the checkbox. Use "/Off" if you want to uncheck.
//"/Yes" is defined in your pdf document as the checked value. May vary depending on original pdf creator.
((PdfDictionary)(((PdfReference)(item)).Value)).Elements.SetName(PdfAcroField.Keys.V, "/Yes");
((PdfDictionary)(((PdfReference)(item)).Value)).Elements.SetName(PdfAnnotation.Keys.AS, "/Yes");
}
}
else
{
currentField.Checked = true;
}
}
catch (NullReferenceException e)
{
// field not found
}
}
}
// create unique file name (UUID)
string tmpName = Guid.NewGuid().ToString() + ".pdf";
string file = destinationFolder + tmpName;
// if file should be printed immediately, add print function to
// pdf document
if(print)
AddPrintFunction(document);
// save the document
document.Save(file);
return file;
}
public static void AddPrintFunction(PdfDocument document)
{
PdfDictionary dict = new PdfDictionary(document);
// According to the PDF Reference the dictionary requires two elements.
// A key /S that specifies the action name, and a key /JS that set the JavaScript to run.
dict.Elements["/S"] = new PdfName("/JavaScript");
dict.Elements["/JS"] = new PdfName("/this.print(true);");
document.Internals.AddObject(dict);
document.Internals.Catalog.Elements["/OpenAction"] = PdfInternals.GetReference(dict);
}
private string getTemplateFile(string templateName)
{
FormLocation template = db.FormLocations.Where(f => f.name == templateName).First();
return template.file;
}
}
}
Ok, I found it out myself. You need to inject IHostingEnvironment, not HostingEnvironment for it to work. I am not experienced enough to explain what each of those is, maybe someone can clarify it in the comments, just wanted to answer the question as I found the problem.

Categories