Asp.net Core File Upload - c#

when i save image, image save successfully in database, but it takes full image path like this C:\Users....\Uploads\employee.jpg i dont want like this, i need to save image path somehting like this ~Uploads\employee.jpg and in specific folder and same path should save in database, also if someone show me after saving correct path how i can view that image. there is error i get because of this:
"Not allowed to load local resource :file:///C:/......"
thank you!
my code:
[HttpPost]
public async Task<IActionResult> Create(Photos photos)
{
if (ModelState.IsValid)
{
var filePath =
Path.Combine(_appEnvironment.ContentRootPath,
"Uploads", photos.FormFile.FileName);
photos.PhotoPath = filePath;
using (var stream = new FileStream(filePath, FileMode.Create))
{
await photos.FormFile.CopyToAsync(stream);
}
_context.Add(photos);
await _context.SaveChangesAsync();
return RedirectToAction(nameof(Index));
}
ViewData["NewsId"] = new SelectList(_context.News, "NewsId",
"NewsBigTitle", photos.NewsId);
return View(photos);
}

Break the code down:
var localPath = Path.Combine("Upload", "file.jpg");
var fullPath = Path.Combine(_appEnvironment.ContentRootPath, localPath);
Save the localPath to PhotoPath
Edit
Okey so now bring up your PhotoPath in a View, and make it target a file stream action.
[HttpGet("path/{image}")]
public FileStreamResult Image(string image)
{
var result = new FileStreamResult(new FileStream(
Path.Combine(_appEnvironment.ContentRootPath, image),
FileMode.Open,
FileAccess.Read), "image/<your_mime>");
return result;
}
The best way I think is to create a new string in the following format http://example.com/path/image.jpg and bind it to src.
You can't target dynamically added files by using the following: ~/path/image.jpg for your source.

Make sure you have configured IFileProvider pointing to your Uploads folder in Configure method of Startup class:
app.UseStaticFiles(); // Default one
// Adding one more location - folder named `Uploads` to be a custom static files folder.
// The `Uploads` folder is available in the content root directory of the application (where your `Controllers` folder.
// You can point it of course inside `wwwroot` - use `env.WebRootPath` instead)
app.UseStaticFiles(new StaticFileOptions {
FileProvider = new PhysicalFileProvider(Path.Combine(env.ContentRootPath, "Uploads")),
RequestPath = new PathString("/Uploads")
});
Once you do this you should be able to upload the file this way in your controller action:
var filePath = Path.Combine(_environment.ContentRootPath, "Uploads", photos.FormFile.FileName);
using (var stream = new FileStream(filePath, FileMode.Create))
{
await photos.FormFile.CopyToAsync(stream);
}

Related

How to save web camera image with ID into wwwroot folder using dotnet core 3.1

Please I need to assist me to solve a problem with save live web camera image into a wwwroot folder with Id of a staff. the code I'm used saving image from web camera with default filename but need to change to save with Id. below is the sample code.
var fileName = file.FileName;
var fileNameToStore = string.Concat(Convert.ToString(Guid.NewGuid()), Path.GetExtension(fileName));
Path.Combine(Directory.GetCurrentDirectory(), "wwwroot\\Photos", fileName);
var filepath = Path.Combine(_environment.WebRootPath, "Photos") + $#"\{fileNameToStore}";
// Save image file in local folder
if (!string.IsNullOrEmpty(filepath))
{
using (FileStream fileStream =
System.IO.File.Create(filepath))
{
file.CopyTo(fileStream);
fileStream.Flush();
}
}
saved with: 03f59a92-65e0-4deb-80a4-b56e91d89d88.png
but I need to save like this: 10001.png
You need to replace:
var fileNameToStore = string.Concat(Convert.ToString(Guid.NewGuid()), Path.GetExtension(fileName));
with:
var fileNameToStore = string.Concat("10001", Path.GetExtension(fileName));
If you want to actually get the right ID at runtime you need some kind of data source for that. User Input? Windows Login? What data is available to you?

Is there any way to upload a folder(have subfolders also) instead of file using .netcore web api

Is it possible to upload the entire folder with same folder structure using .netcore webapi. I know we can zip and upload, then unzip, just wondering for alternative way to upload as it is instead of zipping
Currently i have updated the controller as below, this logic doesnot consider folders for upload
private async Task<bool> Import()
{
bool isSaveSuccess = false;
string fileName;
var files = Request.Form.Files;
foreach (var file in files)
{
try
{
var extn = "." + file.FileName.Split('.')[file.FileName.Split('.').Length - 1];
fileName = file.FileName + DateTime.Now.Ticks + extn;
//fileName = Path.Combine(file.FileName, DateTime.Now.Ticks + extn);
var pathBuilt = Path.Combine(Directory.GetCurrentDirectory(), "Upload\\files1");
if (!Directory.Exists(pathBuilt))
{
Directory.CreateDirectory(pathBuilt);
}
var path = Path.Combine(Directory.GetCurrentDirectory(), "Upload\\files1", fileName);
using (var stream = new FileStream(path, FileMode.Create))
{
await file.CopyToAsync(stream);
}
isSaveSuccess = true;
}
catch
{
_logger.LogError("Request failed: " + "err");
}
}
return isSaveSuccess;
}
Yes, it should be possible. Have a look at webkitdirectory property: https://developer.mozilla.org/en-US/docs/Web/API/HTMLInputElement/webkitdirectory
This will let you select one or more folders, so you can iterate over the inner contents of the users selection. As you can see, this is a "native" feature and not limited to asp.net or c# in any way.
<input type="file" id="files" name="files" webkitdirectory multiple />
<ul id="listing"></ul>
document.getElementById("filepicker").addEventListener("change", function(event) {
let output = document.getElementById("listing");
let files = event.target.files;
for (let i=0; i<files.length; i++) {
let item = document.createElement("li");
item.innerHTML = files[i].webkitRelativePath;
output.appendChild(item);
};
}, false);
As you can see from the demo code, you can get the relative path by calling .webkitRelativePath, so yit should be no problem to recreate the folder structure on your target.
Be aware, that this feature is not natively available on Internet Explorer and Firefox versions on android below 50.
Update for saving files with subfolders
Here is a bare minimum example of uploading taking any number of uploaded form files and saving them to disk, while keeping the relative folder structure.
First change your controllers constructor to get a your current environment in:
private readonly IWebHostEnvironment _env;
public HomeController(IWebHostEnvironment environment)
{
_env = environment;
}
Then amend your upload logic accordingly:
[HttpPost]
public async Task<IActionResult> Upload([FromForm] IFormFileCollection files)
{
//target in wwwroot for static files
string targetFolder = Path.Combine(_env.WebRootPath, "uploads");
foreach (IFormFile file in files)
{
if (file.Length <= 0) continue;
//fileName is the the fileName including the relative path
string path = Path.Combine(targetFolder, file.FileName);
//check if folder exists, create if not
var fi = new FileInfo(path);
fi.Directory?.Create();
//copy to target
using var fileStream = new FileStream(path, FileMode.Create);
await file.CopyToAsync(fileStream);
}
return View("Index");
}
This will result to your folder and all its subfolders being saved into your wwwroot/uploads/ folder inside your application:
wwwroot/
|
|-uploads
|-MyFolder
|- File1.jpg
|- File2.jpg
|- MyFolder2
|- File3.jpg
I'd recommend saving them somewhere different though, because redeploying might clean out the application folder first, depending on your deployment strategy (xcopy, github actions, azure pipelines, containers ...)

Issue of Set Download File name from controller MVC Core

I tried to get and download file from server local folder to client side. But When I tried to download(also download operation is successfully), Downloaded file's name is set automatically Action Name. How can I change downloaded file name ?
MyController:
[HttpGet]
public PhysicalFileResult MYACTIONDOWNLOAD(string filePathAndName)
{
string downloadPath = Path.Combine(Directory.GetCurrentDirectory(), #"C:\", filePathAndName);
string filesMimeType = MimeTypesMap.GetMimeType(filePathAndName);
return new PhysicalFileResult(downloadPath, filesMimeType);
}
client side(view script):
<a target="_blank" href="(my website www root url)/MYPROJECTNAME/MYCONTROLLERNAME/MYACTIONDOWNLOAD?filePathAndName=\\192.168.X.X\MYREMOTEDISC-1\MYDOCUMENTS\SCHOOL\10012021_1023350.docx"></a>
when I clicked download operation is okey but downloaded file name is MYACTIONDOWNLOAD.docx
I want to change file name.
You can just change your code like following:
return new PhysicalFileResult(downloadPath, filesMimeType) { FileDownloadName = "Test.doc"};
You can try this method instead.
[HttpGet]
public async Task<IActionResult> Download(string path)
{
var memory = new MemoryStream();
string webRootPath = _HostEnvironment.WebRootPath;
var uploads = Path.Combine(webRootPath + path);
using (var stream = new FileStream(uploads, FileMode.Open))
{
await stream.CopyToAsync(memory);
}
memory.Position = 0;
var ext = Path.GetExtension(uploads).ToLowerInvariant();
return File(memory, "application/octet-stream", "any file name");
}
_HostEnvironment.WebRootPath will be give the absolute path of the server. If that comes from your URL then you can avoid this.
Instaed of path use
var bytes = File.ReadAllBytes("your path" )
and then
return File(bytes, mimetype,"name you want" );

Dynamic saving of files .net core

I have this function that save the file in the wwwroot foldar:
[HttpPost]
public async Task<IActionResult> UploadFile(IFormFile file)
{
if (file == null || file.Length == 0)
return Content("file not selected");
var path = Path.Combine( Directory.GetCurrentDirectory(), "wwwroot", file.FileName);
using (var stream = new FileStream(path, FileMode.Create))
{
await file.CopyToAsync(stream);
}
return RedirectToAction("Files");
}
I'm trying to save the files dynamically so that every time a file is uploaded the function will check whether there is a folder with the user ID (from the session). If the folder exists, it is saved there, otherwise, it will open a new folder with his ID.
I want to create a sub folder for each userId and save user specific file to that folder
I added this and now it'ts working
var userId = HttpContext.Session.GetString("UserId");
if (!Directory.Exists(Path.Combine(
Directory.GetCurrentDirectory(), $"wwwroot/{userId}")))
{
Directory.CreateDirectory(Path.Combine(
Directory.GetCurrentDirectory(), $"wwwroot/{userId}"));
}
The easiest way to get the current user's id is:
var userId = User.FindFirstValue(ClaimTypes.NameIdentifier);
Then, you just work this into your path:
var path = Path.Combine( Directory.GetCurrentDirectory(), $"wwwroot/users/{userId}", file.FileName);

Issues when downloading PDF file from MVC application

I have created a function where a user can download a pdf file from my webpage. The file is stored in a databse and is requested from a webapi. The return value of the webapi is a byte[].
My issue here is that when i run the web application on my local iis this function runs without any errors. I get the pdf file and it is downloaded correctly on my machine. But when i deploy the web application to my Test server this code generates either RESPONSE_HEADERS_MULTIPLE_CONTENT_DISPOSITION in chrome with some of the files where as other files are downloaded to the machine but when i try to open the pdf file i get: could not load the pdf file.
This happens with both chrome and IE.
This is my code:
[HttpGet]
[DoNotChangeCacheSettings]
public virtual FileResult DownloadTranslationFile(Guid id)
{
Guid assessmentTemplateId = id;
File translationFile = Services.GetFileContent(assessmentTemplateId);
var fileName = HttpUtility.UrlPathEncode(translationFile.FileName);
this.HttpContext.Response.Headers.Add("Content-Disposition", "attachment; filename=\"" + fileName + "\"");
var result = File(translationFile.FileContent.Content, System.Net.Mime.MediaTypeNames.Application.Pdf, fileName);
return result;
}
I have been trying to fix this issue for 2 days now but i simply cant figure out what the issue is. Hope you guys can help. Thanks.
You don't need to use Content-Disposition. .Net will add it for you. From the docs.
The fileDownloadName parameter is used to generate the
content-disposition header. The result object that is prepared by this
method is written to the response by the ASP.NET MVC framework when
the object is executed. The MediaTypeNames class can be used to get
the MIME type for a specific file name extension.
I tend to use the Stream-overload:
[HttpGet]
[DoNotChangeCacheSettings]
public virtual FileResult DownloadTranslationFile(Guid id)
{
Guid assessmentTemplateId = id;
File translationFile = Services.GetFileContent(assessmentTemplateId);
var fileName = HttpUtility.UrlPathEncode(translationFile.FileName);
var stream = = new MemoryStream(translationFile.FileContent.Content);
return File(stream, "application/pdf", fileName);
}
But you can use the byte[] as well:
[HttpGet]
[DoNotChangeCacheSettings]
public virtual FileResult DownloadTranslationFile(Guid id)
{
Guid assessmentTemplateId = id;
File translationFile = Services.GetFileContent(assessmentTemplateId);
var fileName = HttpUtility.UrlPathEncode(translationFile.FileName);
return File(translationFile.FileContent.Content, "application/pdf", fileName);
}
EDIT:
If you got an error when opening the PDF you can ensure that the web browser is doing the right thing by manually saving the PDF from code as well. If that file has errors as well you're probably generating an incorrect byte[].
[HttpGet]
[DoNotChangeCacheSettings]
public virtual FileResult DownloadTranslationFile(Guid id)
{
Guid assessmentTemplateId = id;
File translationFile = Services.GetFileContent(assessmentTemplateId);
var fileName = HttpUtility.UrlPathEncode(translationFile.FileName);
var stream = = new MemoryStream(translationFile.FileContent.Content);
// Code for debugging
var tempDir = "C:\\temp"; // Make sure app pool can write here.
var path = Path.Combine(tempDir, fileName); // Possibly add extension here.
using (var fileStream = File.Create(path))
{
stream.Seek(0, SeekOrigin.Begin);
stream.CopyTo(fileStream);
}
stream.Seek(0, SeekOrigin.Begin);
// Return to client.
return File(stream, "application/pdf", fileName);
}

Categories