I have written an app that should also copy files to Azure Cloud. It works in my development environment, but when I deploy the app to Azure Cloud and launch it from there, it can't access the files on the executing machine to upload them. What can I do, what am I doing wrong?
public ActionResult Index(string fileName)
{
string myIP = GetIP();
string folderPath = #"\\" + myIP + #"\c$\SWAP\";
string containerName = "fileupload";
string connectionString = "DefaultEndpointsProtocol=https;AccountName=...;AccountKey=...;EndpointSuffix=core.windows.net"; var files = Directory.GetFiles(folderPath, "*.xlsx");
BlobContainerClient containerClient = new BlobContainerClient(connectionString, containerName);
foreach (var file in files)
{
var filePathOverCloud = file.Replace(folderPath, string.Empty);
var path = DateTime.Now.ToString() + "_" + filePathOverCloud; try
{
MemoryStream stream = new MemoryStream(System.IO.File.ReadAllBytes(file));
containerClient.UploadBlob(filePathOverCloud, stream);
} catch {
ViewData["Message"] = "File not found ...";
return View();
}
}
return View();
}
OK rickvdbosch, I tried like this, but <IFormFile FileName> always returns NULL ... what am I doing wrong?
````HTML-Page
<div class="text-center">
<h1 class="display-4">File Upload</h1>
</div>
<div class="container">
<div class="mb-3 mt-3">
<form method="post" enctype="multipart/form-data">
<div class="mb-3">
<label class="form-label">Upload File</label>
<span>Select File:</span>
<input type="file" accept=".xlsx" name="FileName" class="form-control" />
</div>
<div class="mb-3">
<button type="submit" class="btn btn-primary">Upload</button>
</div>
</form>
</div>
</div>
````PageController
[HttpPost]
public ActionResult Index(IFormFile FileName)
{
string containerName = "fileupload";
string connectionString = "DefaultEndpoint...fix=core.windows.net";
if (FileName != null)
{
//do upload
}
else
{
//throw error
}
return View();
}
A web application should never, under any circumstance, have access to the filesystem of the client visiting the web app without the user explicitly pointing to files or a location where to get them. Never ever.
Your application needs to be adjusted so that it asks the user which files to upload, and process that information on the post of the page (meaning the user explicitly gave permission to upload the selected file(s)).
Please have a look at Uploading Files in Razor Pages
For the most part, you will use forms to capture data from the user as simple string, numeric, datetime or boolean values. Forms can also be used to upload files. Successful file uploading has three basic requirements:
The form must use the post method
The form must have an enctype attribute set to multipart/form-data
The uploaded file must map to an IFormFile data type
Related
I am trying to embed a pdf file on a razor page. Below is the code in the controller:
[HttpPost]
public ActionResult ViewPDF()
{
string embed = "<object data=\"{0}\" type=\"application/pdf\" width=\"500px\" height=\"300px\">";
embed += "If you are unable to view file, you can download from here";
embed += " or download <a target = \"_blank\" href = \"http://get.adobe.com/reader/\">Adobe PDF Reader</a> to view the file.";
embed += "</object>";
TempData["Embed"] = `string.Format(embed,Url.Content("~/Documents/2022Packet.pdf"));`
return RedirectToAction("Index");
}
public IActionResult Index()
{
return View();
}
Documents is in a folder in my application where I have pdf file called 2022Packet.pdf. below is the screen shot:
This is what I have in my view:
#using (Html.BeginForm("ViewPDF", "PDF", FormMethod.Post))
{
View PDF
<hr />
#Html.Raw(TempData["Embed"])
}
when I run my code, i see this screen shot instead of my pdf file
Is Url.Content("~/Documents/2022Packet.pdf")); that I am using to display the pdf file wrong syntax to display the pdf file? Do I need to use absolutre URL or resolveURL to display the pdf file. This is the resulting HTML on browser:
<div class="container-bg">
<div class="container">
<main role="main" class="pb-3">
<form action="/PDF/ViewPDF" method="post"> View PDF
<hr />
<input name="__RequestVerificationToken" type="hidden" value="CfDJ8CeXypSjYClBh-AvfVG-nwcQ_BEfVjwZg8BdU__NxjoQeke-Tc1mAO5wcURZ8FLNSZPXevyRML7o_nAHh2jwpec04_5ouAXFbTmUdt8YoxCSBy8WtvIMwF7badc7Yd4_blsULWgvxSRfS92lpJ4o7LE" /></form>
</main>
</div>
</div>
Any help will be greatly appreciated.
Is Url.Content("~/Documents/2022Packet.pdf")); that I am using to
display the pdf file wrong syntax to display the pdf file? Do I need
to use absolutre URL or resolveURL to display the pdf file. This is
the resulting HTML on browser:
Well, to begin with your first question, yes its incorrect. In fact, it would not display your file as expected because in your embed html string at data=\"{0}\" you ought to pass file steream instead of file path. Finally, in your string.Format you have to pass your file path from where it would be read. Thus, your pdf has not been displayed.
Solution:
public class PDFController : Controller
{
private readonly IWebHostEnvironment _environment;
public PDFController(IWebHostEnvironment environment)
{
_environment = environment;
}
public IActionResult Index()
{
string path = Path.Combine(_environment.ContentRootPath, "Documents");
// string path = Path.Combine(_environment.WebRootPath, "Documents");
using (MemoryStream stream = new MemoryStream())
{
System.IO.File.WriteAllBytes(path + "/YourFileName.pdf", stream.ToArray());
string embed = "<object data=\"{0}\" type=\"application/pdf\" width=\"500px\" height=\"300px\">";
embed += "If you are unable to view file, you can download from here";
embed += " or download <a target = \"_blank\" href = \"http://get.adobe.com/reader/\">Adobe PDF Reader</a> to view the file.";
embed += "</object>";
TempData["Embed"] = string.Format(embed, "/Documents/YourFileName.pdf");
return View();
}
}
}
Note: If you wanted to read file from ourside of wwwroot use _environment.ContentRootPath but from inside wwwroot use _environment.WebRootPath. As you can see in my example. Then, in string.formeter pass your file path like this string.Format(embed, "/Documents/YourFileName.pdf"); tild ~ is not required.
Output:
Browser Seettings:
If your browser restrict you, in that scenari, you should configure your browsere setting. For Eadge you can configure as following.
Update:
Razor View:
<div class="container-bg">
<div class="container">
<main role="main" class="pb-3">
<a target="_blank" class="btn btn-info" asp-controller="PDF" asp-action="ViewPDF">Download PDF</a>
</main>
</div>
</div>
#Html.Raw(TempData["Embed"])
Download PDF Controller:
While Download PDF would be clicked following controller would called.
public ActionResult ViewPDF()
{
string physicalPath = "wwwroot/Documents/YourPDFFileName.pdf";
byte[] pdfBytes = System.IO.File.ReadAllBytes(physicalPath);
MemoryStream stream = new MemoryStream(pdfBytes);
string mimeType = "application/pdf";
return new FileStreamResult(stream, mimeType)
{
FileDownloadName = "AnyNameYouWantToSet.pdf"
};
}
In my web application, I need to allow the user to choose an image from their machine and upload it, and have that image physically stored to my container in my storage in Azure.
The way to do this I have found in many places and it works for me, but with the IMPORTANT EXCEPTION that in all examples, they upload a file from their own machine and knowing or harcoding the physical path.
But as for security reasons, the input file cannot read the physical address of the client, I was forced to use the strategy of:
I upload the image to the web server to a known path.
I take that path, and I use it to upload to Azure Storage Blob.
Confirmed this, I don't want that file to stay on the server, I just want it to be in my container, so I proceed to delete the file.
I get the following exception:
The process cannot access the file.
'C:\My_folders\MyFile.jpg' because it is being used by another process.
Note: I tried using a project directory and also the temporary directory of the operating system. For both cases I get the same result.
So, in conclusion: I need to upload to Azure S Blob, but as far as I could find out, for that I need the physical path of the file, so I need to upload it to my server and from there upload it.
I would like you to help me with these options:
To be able to upload it without the physical path.
Or to be able to close the process I have now, ensuring the deletion of the temporary file on the server.
Any of your suggestions.
Thank you very much!
_________________________ CODE ________________________
FRONT
<form method="POST" enctype="multipart/form-data" id="fileUploadForm">
<div class="row">
<div class="col-md-12" id="conten_CargaImagenes">
<div class="card card-success">
<div class="card-header">
<h3 class="card-title" id="tituloAbmHorarios">
<i class="far fa-image"></i> Nueva Imagen
</h3>
</div>
<div class="card-body">
<div class="row">
<div class="col-md-12">
<div class="form-group">
<input asp-for="File" class="form-control custom-file-input" />
<label id="fileImageLabel" asp-for="File" class="custom-file-label "></label>
</div>
</div>
<div class="col-md-3">
<button type="button" class="btn bg-gradient-success btn-sm pull-left" onclick="GuardarImagen()">
<i class="fas fa-file-upload"></i> Grabar Imagen
</button>
</div>
</div>
</div>
</div>
</div>
</div>
JS Method
function GuardarImagen() {
var form = $('#fileUploadForm')[0];
var model = new FormData(form);
model.append('IdComercio', $('#Id').val());
$.ajax({
type: "POST",
enctype: 'multipart/form-data',
url: "/Comercios/GrabaFile",
data: model,
processData: false,
contentType: false,
cache: false,
timeout: 600000,
success: function (e) {
if (!e.isError) {
$("#File").val(null);
document.getElementById("fileImageLabel").innerHTML = '';
toastr.success(textos.imagenGrabadaOk, { timeOut: 2000 });
InjectarNuevaImagenEnPantalla(e.data);
} else {
toastr.error(e.data, { timeOut: 2000 });
}
}
});
}
BACK
[HttpPost]
public ReturnData GrabaFile(UploadFileComercioDTO pData)
{
if (pData.File != null)
{
try
{
var result = UploaderFilesService.UploadToAzure(pData.File, FolderPath, pData.IdComercio.ToString());
}
catch (Exception ex)
{
do something...
}
}
else
{
return messageError
}
}
public static ReturnData UploadToAzure(IFormFile pFile, string pFolder, string pIdComercio)
{
ReturnData returnData = new();
if (pFile != null)
{
if (ValidaFile(pFile.ContentType, pFile.Length))
{
try
{
string nombreOriginal = pFile.FileName;
var nombrePartes = nombreOriginal.Split(".");
string extension = nombrePartes[(nombrePartes.Length - 1)];
string nombreFinal = GenerarTextoUnico() + "." + extension;
string soloNOmbre = nombreFinal; // ****
pFolder = Path.GetTempPath(); // *************
string filePath = Path.Combine(pFolder, nombreFinal);
using (var stream = File.Create(filePath))
{
pFile.CopyTo(stream);
}
string connectionString = "adsfasfasdfasdf";
string containerName = "asdfasdf";
nombreFinal = pIdComercio + "/" + nombreFinal;
BlobContainerClient container = new BlobContainerClient(connectionString, containerName);
var upload = container.UploadBlob(nombreFinal, File.OpenRead(filePath));
try
{
File.Delete(filePath);
}
catch (IOException e)
{
Console.WriteLine(e.Message);
}
}
catch (Exception ex)
{
...
}
}
else
{
...
}
}
else
{
...
}
return returnData;
}
I found the problem and the solution.
The problem was that it was not closing the stream when uploading.
The modification to make was in the UploadToAzure method, change the following line
var upload = container.UploadBlob(finalName, File.OpenRead(filePath));
for this
using (var stream = File.OpenRead(filePath))
{
var upload = container.UploadBlob(endName, stream);
}
That way when I exit using the stream is already closed and I can proceed to delete it.
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....
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);
}