I have seen so many working examples of File uploading with MVC.
However, I want to follow a different approach such that, I want a little abstraction as follows:
I want to introduce a FileService, and inject that to the controller as a dependency. Let the service upload the file and return me a UploadedFile object.
A problem I am having right now is to upload to correct place/directory in file system or application root.
In a controller, I have access to Server object which I can call Server.MapPath and it does the magic, below I cant access to that Object since it is not a Controller.
How can I upload to anywhere in file system or in project root below?
public class FileService : IFileService
{
private const string UploadBase = "/Files";
public File UploadFile(HttpPostedFileBase file)
{
if (file != null)
{
string folder = DateTime.Today.Month + "-" + DateTime.Today.Year;
string finalFolder = Path.Combine(UploadBase, folder);
if (!Directory.Exists(finalFolder))
{
return Directory.CreateDirectory(finalFolder);
}
var filename = UploadFile(file, directoryInfo.Name + "/");
var newFile = new File { ContentType = file.ContentType, FilePath = filename, Filename = file.FileName };
return newFile;
}
return null;
}
An error is :
The SaveAs method is configured to require a rooted path, and the path '9-2013/037a9ddf-7ffe-4131-b223-c4b5435d0fed.JPG' is not rooted.
Re-stating what was noted in comments:
If you want to map the virtual path to the physical path outside of the controller, you can always use HostingEnvironment.MapPath method.
Related
I'm making a converter that receives some image/video and does the process to turn it into a webp file.
With jpg, png and webm files i don't have any problem at all, but for some reason when I attempt to use a gif file I got this error: "Access to the path 'C:\Users\Desktop\computing\api\wwwroot\spin.gif' is denied."
This error occurs when i`m trying to save the file received by a IFormFile.
My controller is like this:
[HttpPost]
[DisableRequestSizeLimit]
public async Task<IActionResult> ConverterToWebp()
{
var webp = new WEBPConverter(new VideoSettings(_videoSettings));
var workingdir = Path.Combine(Directory.GetCurrentDirectory(), "wwwroot");
var (file, _) = GetFile();
if (file == null)
return BadRequest("Arquivo inválido");
var (filepath, _) = await SaveToDir(file, workingdir);
var res = await webp.ConverterWebp(filepath);
if (!res.Success)
return BadRequest(res);
return File(res.bytes, "image/webp");
}
The method GetFile() look like this:
private (IFormFile? file, string? mimetype) GetFile()
{
var files = HttpContext.Request.Form.Files;
var file = files.FirstOrDefault();
if (file == null || file.Length == 0)
return (null, null);
var contentTypeProvider = new FileExtensionContentTypeProvider();
var isBlob = file.FileName.ToLower() == "blob";
var mimetypeParseOk = contentTypeProvider.TryGetContentType(file.FileName, out var mimeType);
if (isBlob)
return (file, "blob");
if (mimetypeParseOk)
return (file, mimeType);
return (null, null);
}
And the method who trigger the error, SaveToDir(), look like this:
private async Task<(string filepath, string filename)> SaveToDir(IFormFile file, string path)
{
var filename = new string(file.FileName
.Replace(' ', '_')
.Normalize(NormalizationForm.FormD)
.Where(ch => char.GetUnicodeCategory(ch) != UnicodeCategory.NonSpacingMark)
.ToArray());
var filepath = Path.Combine(path, filename);
using var stream = new FileStream(filepath, FileMode.Create);
await file.CopyToAsync(stream);
return (filepath, filename);
}
The entire project is using .net core 6.0
If I take one file with .gif extension and change it to .webm I got no error, even though the conversion don`t works great.
I don't know the reason why only if i use gif this method to save in directory don't work and generate that generic error because the path exist and has permissions, and that's why it doesn't trigger error in other files types.
By default IIS does not have permission for the wwwroot folder. You need to grant permissions to the IIS_IUSRS for the folder. I would not recommend this approach as it may be a potential security risk. The approach you could take is:
string path = Path.Combine(Path.GetTempPath(), Path.GetTempFileName());
With this you would save the file to the temp folder and temp file name in the users temp folder, in this case the assigned application user or by default IIS_IUSRS. Don't forget to delete the file after you're done with it.
In the case you want to go with the path of granting the access you do the following:
Go to where your inetpub folder is located
Right click on wwwroot and click on Properties
Switch to the security tab and click Edit...
Look for the IIS_IUSRS user, should be PCNAME\IIS_IUSRS
Select Allow for all permissions.
In the end I managed to solve the problem by saving a file without extesion and using theirs bytes arrays to convert.
But in fact the original question is not solved, any file that I try to use and that has the gif's extension get error. I tried to test changing a webm file that's working with name "test.webm" to "test.gif" and get the same error of permission.
This is how my method got no error:
private async Task<(string filepath, string filename)> SaveToDir(IFormFile file, string path)
{
var timespanEpoch = (int)(DateTime.UtcNow - new DateTime(1970, 1, 1)).TotalSeconds;
var filename = $"web_temp_file-{timespanEpoch}";
var filepath = Path.Combine(path, filename);
using var stream = new FileStream(filepath, FileMode.Create);
await file.CopyToAsync(stream);
return (filepath, filename);
}
Is there a way to load video file to video player without specifying the name and extension? Or at least not the extension of the video file.
vp.url = Application.streamingAssetsPath + "/Video/" + "VideoFile.mp4";
No, the url expects an URL or a System path.
The file or HTTP URL that the VideoPlayer reads content from.
File URLs are filesystem paths that are either absolute on the platform or relative to the Player root.
A valid system path includes the extension of a file if it has one.
You could however use Directory.GetFiles in order to find files by partial name in a given folder like
using System.Linq;
using System.IO;
private string FindVideoFile(string folderPath, string partialFileName)
{
if(!Directory.Exists(folderPath)
{
Debug.LogError($"The folder {folderPath} does not exist!", this);
return null;
}
var result = Directory.GetFiles(folderPath, partialFileName + "*", SearchOption.TopDirectoryOnly).FirstOrDefault();
return result;
}
And use it like
var folder = Path.Combine(Application.streamingAssetsPath, "Video");
var fileName = "VideoFile";
var fullPath = FindVideoFile(folder, fileName);
if(!fullPath.IsNullOrWhiteSpace())
{
vp.url = fullPath;
}
else
{
Debug.LogError($"No file called {fileName} was found in folder {folder} ", this);
return;
}
Typed on smartphone but I hope the idea gets clear
I am new to development in ASP.NET Core. I am attempting to set up an app that allows specific users to upload files to my webserver. I have successfully uploaded files to a temp directory but would like to save files in 'wwwroot/images' instead of a temporary directory.
Here is a listing of the controller handling the file uploading:
[HttpPost("FileUpload")]
[DisableFormValueModelBinding]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Index(List<IFormFile> files)
{
long size = files.Sum(f => f.Length);
var filePaths = new List<string>();
foreach (var formFile in files)
{
if (formFile.Length > 0)
{
/*full path to file in temp location*/
var filePath = Path.GetTempFileName();
filePaths.Add(filePath);
using (var stream = new FileStream(filePath, FileMode.Create))
{
await formFile.CopyToAsync(stream);
}
}
}
/*
* Process uploaded files. Don't rely on or trust the FileName
* property without validation.
*/
return Ok(new { count = files.Count, size, filePaths });
}
As you can see, the controller stores the uploaded file to a temp directory. Can anyone show me how to manually specify a location to store file in?
Startup.cs contains the following code:
/*To list physical files from a path provided by configuration:*/
var physicalProvider = new PhysicalFileProvider(Configuration.GetValue<string>("StoredFilesPath"));
services.AddSingleton<IFileProvider>(physicalProvider);
I believe these two lines allow me to specify which directory I would like the uploaded files saved to, as a string in my appsettings.json file:
/wwwroot/images
As stated above, I'm still very new to ASP.NET Core web development. So, if I have neglected to include any pertinent information, please let me know what's missing from my posting and I'll do my best to update my listing. If anyone could provide me with some direction on how to achieve this functionality, I would greatly appreciate it.
Thank you for your help.
You don't need PhysicalFileProvider in this case. You can easily inject IWebHostEnvironment (in previous asp.net core versions was IHostingEnvironment and now is deprecated).
public string _rootPath;
public HomeController(IHostingEnvironment env)
{
_rootPath = env.WebRootPath;
}
Then in your Index action method just replace this line:
var filePath = Path.GetTempFileName();
with this:
var filePath = Path.Combine(_rootPath, formFile.FileName);
In Path.Combine if you want to store somewhere in wwwroot, and do that dynamically then add one more parameter between _webRootPath and fileName in format files\\images\thumbnails or files/images/thumbnails. This will store data in your wwwroot/files/images/thumbnails.
This example is specific for wwwroot if you want to store to some another directory outside then change line _rootPath = env.WebRootPath; to _rootPath = env.ContentRootPath;
IHostingEnvironment has webRoot which gives the path for wwwroot. Append the file name with webRoot to get the filePath.
string folder = env.webRoot;
string fileName = ContentDispositionHeaderValue.Parse(formFile.ContentDisposition).FileName.Trim('"');
string fullPath = Path.Combine(folder, fileName);
Note: env is injected in constructor as parameter.
I have an app written using c# on the top on ASP.NET MVC 5 framework. One of my pages allow a user to upload file to the server. So I use HttpPostedFileBase to upload the file to the server.
However, instead on saving the file to a permanent place, I am hoping be able to extract the fullname of the file and work on it before moving it to a permanent place.
How can I get the temp full-name of the uploaded file?
I tried the following, but the check File.Exists(tempFullname) always fails.
public string GetFullname(HttpPostedFileBase file)
{
string tempFullname = Path.GetTempPath() + file.FileName;
if(File.Exists(tempFullname))
{
return tempFullname;
}
return string.Empty;
}
I also tried the following but temp.Length throw an exception as the file does not exists
public string GetFullname(HttpPostedFileBase file)
{
string temp = new FileInfo(file.FileName);
if(temp.Length > 0)
{
return tempFullname;
}
return string.Empty;
}
To get the full name of need to save the file first, i can't get the location of a file if you don't save it first!
var filePath = Path.Combine(Server.MapPath("<A folder you want>"), file.FileName);
file.Save(filePath);
In this way, filePath is the full path of your file
if (FileUpload1.HasFile)
{
string FileName = Path.GetFileName(FileUpload1.PostedFile.FileName);
//string path = Server.MapPath(#"~\\"+Session["parentfolder"].ToString() +"\\"+ Session["brandname"].ToString() + "\\" + Seasonfolders.SelectedItem.Text + "\\" + stylefolders.SelectedItem.Text + "\\Images\\" + FileName);
string root = Server.MapPath("~");
string path = Path.GetDirectoryName(root);
string path1 = Path.GetDirectoryName(path);
string rootfolder = Path.GetDirectoryName(path1);
string imagepath = rootfolder + Session["brandname"].ToString() + "\\" + Seasonfolders.SelectedItem.Text + "\\" + stylefolders.SelectedItem.Text + "\\Images\\" + FileName;
FileUpload1.SaveAs(imagepath);
//objBAL.SaveImage("Image", Session["brandname"].ToString(), Seasonfolders.SelectedItem.Text, stylefolders.SelectedItem.Text, FileName, imagepath, Convert.ToInt32(Session["Empcode"]));
uploadedimage.ImageUrl = Server.MapPath(#"~/"+imagepath);
uploadedimage.DataBind();
}
uploadedimage is ID for Image control. The path of imagepath is E:\Folder1\Folder2\Folder3\Images\1.png
The image is getting saved but I cannot able to display the uploaded image. Do I need to add anything in this line which is to display an image ..
uploadedimage.ImageUrl = Server.MapPath(#"~/"+imagepath);
uploadedimage.DataBind();
Hosting websites or stuff on iis, does not work this way. There are a few concepts one needs to learn on that front, but the best place to start with is understand what is virtual directory.
One quote from the this page:
In IIS 7, each application must have a virtual directory, known as the
root virtual directory, and maps the application to the physical
directory that contains the application's content
So it means this is a directory where the application's "content" reside; it could be simple text files, images, etc, to complex server side pages like aspx or even classic asp, or php, etc. Now anything out of this directory is not accessible by the hosted web application.
Hence the path you intend to share doesn't work that way. There are a few options to handle this scenario.
In iis you could create a sub-virtual directory, and map its path to where your images are stored to the physical location where the images reside.
Provided your web application (when hosted on iis) has access to the path where the images reside, you can write code to read the file, and send the stream of bytes back so that your web page can render image properly.
2nd approach is usually implemented by a handler (ashx) via which you can pass the image name as query string argument, and return the bytes. Hence in short you do something like this:
uploadedImage.ImageUrl = "~/MyImageHandler.ashx?filename=foo.png" //in ur server code.
In the handler you write something like this:
public class MyImageHandler : IHttpHandler {
public void ProcessRequest (HttpContext context) {
// Comment out these lines first:
// context.Response.ContentType = "text/plain";
// context.Response.Write("Hello World");
context.Response.ContentType = "image/png";
var filepath = #"E:\your_image_dir\" + Request.QueryString["filename"];
//Ensure you have permissions else the below line will throw exception.
context.Response.WriteFile(filepath);
}
public bool IsReusable {
get {
return false;
}
}
}
The image Url in your image should be like this
"~/"+ imagepath
Try removing Server.MapPath
Try this
Create Data Folder in your root directory
if (FileUpload1.HasFile)
{
string FileName = Path.GetFileName(FileUpload1.PostedFile.FileName);
string imagepath =Server.MapPath("~/Data/"+FileName);
FileUpload1.SaveAs(imagepath);
uploadedimage.ImageUrl="~/"+imagepath;
}