With my controller I am able to upload files to certain path. I am trying to figure out how to delete iterated file in my view.
Controllers method:
[Authorize(Roles = "Moderatorzy")]
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult DeleteFile(string file)
{
if (!System.IO.File.Exists(file))
{
return NotFound();
}
System.IO.File.Delete(file);
return View("Edit");
}
View file:
<form asp-action="Edit" method="post" enctype="multipart/form-data">
<input type="hidden" asp-for="ID" />
(...)
#if (Enumerable.Count(ViewBag.fileList) != 0)
{
<dir>Files to download:</dir>
{
foreach (var file in ViewBag.fileList)
{
<a class="down" href="Autobus/DeleteFile?file=#(ViewBag.fileDirectory + file)"><dir>#file<span>;</span></dir></a>
}
}
}
(...)
<div class="text-center">
<button class="btn btn-success" type="submit">Zapisz</button>
Powrót
</div>
<div class="space"></div>
Right now I am having 2 issues:
1) Autobus is the controller name. href="Autobus/DeleteFile?file=#(ViewBag.fileDirectory + file)" gives me path: /Autobus/Autobus/DeleteFile(...) instead of /Autobus/DeleteFile(...). Why?
2) After typig manually just one Autobus it does not call DeleteFile method. Why?
Complete generated route path is: http://localhost:50686/Autobus/Autobus/DeleteFile?file=C:\Users\asus\Desktop\Praca%20IT\Programowanie\Projekty\DluzynaSzkola\ASP.NET%20Core%20-%20ostatni\Dluzyna_Szkola_2\BasicConfig\wwwroot/uploaded/bus/1.jpg
P.S. I am guessing it might be something wrong with routing.
My final working solution:
View file:
(...)
#if (Enumerable.Count(ViewBag.fileList) > 0)
{
<dir>Wgrane już pliki:</dir>
{
foreach (var someFile in ViewBag.fileList)
{
<form asp-action="DeleteFile" method="post">
#Html.AntiForgeryToken()
<input type="hidden" name="file"value="#someFile" asp-action="#(ViewBag.fileDirectory + someFile)" />
<button class="btn btn-danger" type="submit">Usuń</button>
#someFile
</form>
}
}
}
(...)
Also in my DeleteFile method I had to add ViewBags:
[Authorize(Roles = "Moderatorzy")]
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult DeleteFile(string file)
{
string fileDirectory = Path.Combine(
Directory.GetCurrentDirectory(), "wwwroot/uploaded/bus/");
ViewBag.fileList = Directory
.EnumerateFiles(fileDirectory, "*", SearchOption.AllDirectories)
.Select(Path.GetFileName);
ViewBag.fileDirectory = fileDirectory;
string webRootPath = _hostingEnvironment.WebRootPath;
var fileName = "";
fileName = file;
var fullPath = webRootPath + "/uploaded/bus/" + file;
if (System.IO.File.Exists(fullPath))
{
System.IO.File.Delete(fullPath);
ViewBag.deleteSuccess = "true";
}
return View("Edit");
}
This part of your complete generated path:
C:\Users\asus\Desktop\Praca%20IT\Programowanie\Projekty\DluzynaSzkola\ASP.NET%20Core%20-%20ostatni\Dluzyna_Szkola_2\BasicConfig\wwwroot/uploaded/bus/1.jpg
is because of ViewBag.fileDirectory in this line of code ->
<a class="down" href="Autobus/DeleteFile?file=#(ViewBag.fileDirectory + file)"><dir>#file<span>;</span></dir></a>
You use your real path of the file in your server (local computer) for your view and this can make a lot of problems. You should define something like id for each file and then send id of that file to your controller action method and after that then realize this id is for which file and finally delete that file.
So you must change your code like this:
In this situation the name of your file is id. Although this is not the
standard way. We do this just for learning purpose.
Change this line of your code ->
<a class="down" href="Autobus/DeleteFile?file=#(ViewBag.fileDirectory + file)"><dir>#file<span>;</span></dir></a>
With this line ->
#Html.ActionLink( $"Delete {file}", "DeleteFile", "Autobus", new { file = file}, new { })
Now when you click on each of generated links in your browser, your action method DeleteFile will receive the file name. and then if you know which directory are your files, you can delete it in your DeleteFile action method with one line of code like this
System.IO.File.Delete(fileDirectory+file);
Notice: If your fileDirectory path is something like this
C:\Users\asus\Desktop\Praca%20IT\Programowanie\Projekty\DluzynaSzkola\ASP.NET%20Core%20-%20ostatni\Dluzyna_Szkola_2\BasicConfig\wwwroot/uploaded/bus/1.jpg
your action mehtod (DeleteFile) will throw an exception. So you must
change your code in this way:
string fullPath = Request.MapPath("~/uploaded/" + file);
if (System.IO.File.Exists(fullPath))
{
System.IO.File.Delete(fullPath);
}
In this code ~ specify root of your asp.net MVC application and uploaded folder is a folder that you use for locating your files (you can change it to your files folder).
If you change your code in this way you may have some little problems at first but the concept is right and with a little change you can do what you want.
I Hope This Answer Can Help You....
Related
I have a ASP.NET MVC project in which the user can upload multiple files at a time. The following code is in the View:
#using (Html.BeginForm("Edit",
"Bacteria",
FormMethod.Post,
new { enctype = "multipart/form-data" }))
{
#Html.AntiForgeryToken()
<!--Other fields that are posted correctly to the db-->
<div class="">
<label class="control-label col-md-2">Attach New Files:</label>
<div class="col-md-10">
<input type="file" id="Attachment" name="Attachment" class="form-control" accept=".xls,.xlsx,.csv,.CSV,.png,.jpeg,.jpg,.gif,.doc,.docx,.pdf,.PDF" multiple />
</div>
</div>
<div class="form-group">
<div class="col-md-offset-2 col-md-10">
<input type="submit" value="Save" class="btn btn-default" />
</div>
</div>
The method in the controller is:
if (ModelState.IsValid)
{
db.Entry(bacteria).State = EntityState.Modified;
db.SaveChanges();
foreach (string file in Request.Files)
{
HttpPostedFileBase hpf = Request.Files[file];
var fileName = Path.GetFileName(hpf.FileName);
if (fileName == null || fileName == "")
{
break;
}
var subPath = "Attachments/Bacteria/" + bacteria.ID + "/";
bool exists = System.IO.Directory.Exists(Server.MapPath("~/" + subPath));
if (!exists)
{
System.IO.Directory.CreateDirectory(Server.MapPath("~/" + subPath));
}
var path = Path.Combine(subPath, fileName);
hpf.SaveAs(Server.MapPath("~/" + path));
BacteriaAttachment a = new BacteriaAttachment()
{
Name = fileName,
Bacteria = bacteria,
Link = path
};
db.BacteriaAttachments.Add(a);
db.SaveChanges();
}
}
If I upload FileOne.png, FileTwo.png, FileThree.png; the BacteriaAttachements table will get 3 new records with all of them having the same name (such as FileOne.png), link and bacteriaID. Only their ID (the primary key) is unique. And only one file (E.g: FileOne.png) gets uploaded in the server.
So instead of the three files being uploaded, only one of them is being uploaded thrice.
Any help is much appreciated.
Thank you.
When you execute, foreach (string file in Request.Files), for each iteration, the value of file will be the string value "Attachment", which is the name of your file input. When user uploads multiple files from the same input, Request.Files stores all of them with the same key - the name of your input element, which is "Attachment". Now when you execute Request.Files["Attachment"], it will give you only the first item (because all items has same key). For all iterations of the loop, this is what happening.
When accessing Request.Files, do not use name based access approach, Use index based approach (Request.Files[zeroBasedindex]).
You can use a for loop to properly iterate through the collection and read Request.Files using index based approach.
for(var i = 0; i < Request.Files.Count; i++)
{
HttpPostedFileBase hpf = Request.Files[i];
// Your existing code to save hpf.
}
I personally always use a collection of HttpPostedFileBase as my HttpPost action method parameter or as a property in my view model (which i will use a paramater as my HttpPost action method) and loop that. The important thing to remember is, your parameter/property name should match with the name of the input you are using for file upload.
public ActionResult Save(List<HttpPostedFileBase> attachment)
{
foreach(var hpf in attachment)
{
// to do : save hpf
}
// to do : return something
}
Mudassar Ahmed Khan has explained it really well here: https://www.aspsnippets.com/Articles/MVC-HttpPostedFileBase-multiple-files-Upload-multiple-files-using-HttpPostedFileBase-in-ASPNet-MVC.aspx
You should consider using List<HttpPostedFileBase> as method parameter in your controller method. And then loop through each of them. Something like below;
foreach (HttpPostedFileBase postedFile in postedFiles)
{
if (postedFile != null)
{
string fileName = Path.GetFileName(postedFile.FileName);
Hope this is helpful!
I am using kendo mobile to build a mobile application in which the user will be able to click and upload a photo. When they first enter the page, it will show their current photo, I want to be able to click and open file explorer on their device and have the ability to show a preview of their photo in place of the old one. Then when the click done it will send it to my MVC controller where I can then send it to where I want. I cant figure out how to send my file to the controller.
HTML
<div id="NewAccountUploadContainer">
<img id="NewAccountUpload" src="~/Images/btnCamera.png" data-bind="click: uploadPhoto" />
#using (Html.BeginForm("SendNewPhoto", "MobilePlatform", FormMethod.Post, new { enctype = "multipart/form-data" }))
{
<input id="ImageUploadBtn" style="display: none" type="file" accept="image/*" />
<input type="submit" value="OK" style="display: none" />
}
<div id="ImgUploadTxt" data-bind="click: uploadPhoto">
Upload a<br />
different photo.
</div>
The #ImageUploadBtn will be triggered by the #NewAccountUpload or #ImgUploadTxt clicks in jquery which works, but I cant get it to display a file or send to my controller when I trigger the submit.
C# Controller
[HttpPost]
public ActionResult SendNewPhoto(HttpPostedFileBase file)
{
// Verify that the user selected a file
if (file != null && file.ContentLength > 0)
{
// extract only the fielname
var fileName = Path.GetFileName(file.FileName);
// store the file inside ~/App_Data/uploads folder
var path = Path.Combine(Server.MapPath("~/App_Data/uploads"), fileName);
file.SaveAs(path);
}
// redirect back to the index action to show the form once again
return RedirectToAction("Index");
}
The file is always null at this point.
I'm using the Kendo for mvc4, and a mobile implementation, and I'm using the follow code, works for me:
View:
#(Html.Kendo().Upload()
.Name("files")
)
Controller
public ActionResult Submit(IEnumerable<HttpPostedFileBase> files)
{
if (files != null)
{
TempData["UploadedFiles"] = GetFileInfo(files);
}
return RedirectToAction("Result");
}
public ActionResult Result()
{
return View();
}
private IEnumerable<string> GetFileInfo(IEnumerable<HttpPostedFileBase> files)
{
return
from a in files
where a != null
select string.Format("{0} ({1} bytes)", Path.GetFileName(a.FileName), a.ContentLength);
}
I wrote some code in MVC a year ago and my knowledge of the framework seems to have vanished. In the code block below, I list all the files in a directory and provide a link to download them (for authenticated users). What I want to do is give the option to delete each file as well. I just added a delete button, but I am not sure where to go from there?
#{IEnumerable<string> enumerateFiles = Directory.EnumerateFiles(Server.MapPath("~/Content/Documents"));}
#{
if (Request.IsAuthenticated)
{
<h3>Authenticated User: #User.Identity.Name</h3>
<h4>-Downloadable Files-</h4>
<ul>
#foreach (var fullPath in enumerateFiles)
{
var fileName = Path.GetFileName(fullPath);
<li> #fileName
<button type="button" id="fileRemover" value="Delete" onclick="return confirm('Are you sure?')" >Delete</button>
</li>
}
</ul>
}
else
{
<h3>Non-Authenticate User, register and/or login to see documents</h3>
}
}
The code for viewing files and deleting files should be contained within the controller. Your view is meant for simply displaying information (usually from your model) back to the user.
If I were you, I would structure my controller like this:
public class FilesController : Controller
{
public ActionResult List()
{
List<FileInfo> model = new List<FileInfo>();
// Grab all file names from the directory and place them within the model.
return View(model);
}
public ActionResult View(string fileName)
{
// Add header for content type
// Grab (and verify) file based on input parameter fileName
return File(...);
}
public ActionResult Delete(string fileName)
{
// Verify file exists
// Delete file if it exists
return RedirectToAction("List");
}
}
The filename should come as a HTTP POST variable.
So you must create an hidden field to hold the file name so that you can access the value on the action when the form is submitted.
Above the action name you would use the [HttpPost] attribute so that form submit lands on this action.
It is safe to have a HTTP POST instead of HTTP GET otherwise anyone with a url will be able to delete a file.
If you have multiple file names, then each hidden field can have the name as filename_1, filename_2 etc.
I have given you the direction where to look & investigate.
I am trying to create an application that will display images that are stored locally on the webserver. Here is what I have in my view, note that "entry" are absolute addresses like "C:\Images\Image1.jpg". However, when I run it, I get "Not allowed to load local resource: file:///C:/Images/ImageName.jpg" in the console log. So maybe it tries to access the image on the client. How do I tell my view to access the local webserver path and not look for the image source on the client? Please note that moving the images into project directory is not an option, because the images are stored on a different drive on the webserver.
<!-- language: c# -->
#model List<String>
<div style="height: 500px; overflow:scroll;">
<h2>
ScreenShots for testMachine</h2>
#foreach (var entry in Model)
{
<div class="nailthumb-container square-thumb">
<img alt="screenshot" src="#Url.Content(entry)" />
</div>
}
</div>
You cannot directly serve images outside of your ASP.NET MVC 3 application to the client. That would be a huge security vulnerability if the client could access arbitrary files on your server.
You will need to write a controller action that will return them and then point your src property of your <img> tags to this controller action.
public class ImagesController: Controller
{
public ActionResult SomeImage()
{
return File(#"C:\Images\foo.jpg", "image/jpeg");
}
}
and inside your view:
<img src="#Url.Action("SomeImage", "Images")" alt="" />
You could also pass the image name as parameter to the controller action:
public class ImagesController: Controller
{
public ActionResult SomeImage(string imageName)
{
var root = #"C:\Images\";
var path = Path.Combine(root, imageName);
path = Path.GetFullPath(path);
if (!path.StartsWith(root))
{
// Ensure that we are serving file only inside the root folder
// and block requests outside like "../web.config"
throw new HttpException(403, "Forbidden");
}
return File(path, "image/jpeg");
}
}
and in your view:
<img src="#Url.Action("SomeImage", "Images", new { image = "foo.jpg" })" alt="" />
The above code was useful for me, with a change like this
System.Web.UI.Page page = new System.Web.UI.Page();
string filePath = page.Server.MapPath("~/Log/" + fileName);
if (!filePath.StartsWith(filePath))
{
throw new HttpException(403, "Forbidden");
}
return File(filePath, "Content-Disposition", "attachment;filename=TableImportLog.csv");
}
the file thrown to the user is with file name like this "attachment;filename=TableImportLog.csv", but i want the file name as "TableErrorLog.csv"
need help for the same!
I'm trying to make uploading a file to the server at my mvc project. I wrote my class,
public class MyModule: IHttpModule
which defines the event
void app_BeginRequest (object sender, EventArgs e)
In it, I check the length of the file that the user has selected to send.
if (context.Request.ContentLength> 4096000)
{
//What should I write here, that file is not loaded? I tried
context.Response.Redirect ("address here");
//but the file is still loaded and then going on Redirect.
}
In ASP.NET MVC you don't usually write http modules to handle file uploads. You write controllers and inside those controllers you write actions. Phil Haack blogged about uploading files ni ASP.NET MVC:
You have a view containing a form:
<% using (Html.BeginForm("upload", "home", FormMethod.Post,
new { enctype = "multipart/form-data" })) { %>
<label for="file">Filename:</label>
<input type="file" name="file" id="file" />
<input type="submit" />
<% } %>
And a controller action to handle the upload:
[HttpPost]
public ActionResult Upload(HttpPostedFileBase file)
{
if (file != null && file.ContentLength > 0)
{
if (file.ContentLength > 4096000)
{
return RedirectToAction("FileTooBig");
}
var fileName = Path.GetFileName(file.FileName);
var path = Path.Combine(Server.MapPath("~/App_Data/uploads"), fileName);
file.SaveAs(path);
}
return RedirectToAction("Index");
}