How do I link to downloadable files in ASP.NET MVC? - c#

I am new to ASP.NET MVC and I'am trying to link to downloadable files (.zip, .mp3, .doc, etc).
I have the following view: ProductName
which maps to: http://domain/ProductName
I have a .zip file that must map to URL http://domain/ProductName/Product.zip
Questions
Where do I place this .zip file in the MVC folder structure?
How do I add link to this .zip file in MVC? Is there a Url.* method that do this?

You can use FilePathResult or Controller.File method.
protected internal virtual FilePathResult File(string fileName, string contentType, string fileDownloadName) {
return new FilePathResult(fileName, contentType) { FileDownloadName = fileDownloadName };
}
Sample code action method.
public ActionResult Download(){
return File(fileName,contentType,downloadFileName);
}
Hope this code.

The following class adds a file DownloadResult to your program:
public class DownloadResult : ActionResult
{
public DownloadResult()
{
}
public DownloadResult(string virtualPath)
{
this.VirtualPath = virtualPath;
}
public string VirtualPath { get; set; }
public string FileDownloadName { get; set; }
public override void ExecuteResult(ControllerContext context)
{
if (!String.IsNullOrEmpty(FileDownloadName))
{
context.HttpContext.Response.AddHeader("content-disposition",
"attachment; filename=" + this.FileDownloadName);
}
string filePath = context.HttpContext.Server.MapPath(this.VirtualPath);
context.HttpContext.Response.TransmitFile(filePath);
}
}
To call it, do something like this in your controller method:
public ActionResult Download(string name)
{
return new DownloadResult
{ VirtualPath = "~/files/" + name, FileDownloadName = name };
}
Note the virtual path, which is a files directory in the root of the site; this can be changed to whatever folder you want. This is where you put your files for download.
Check this tutorial about Writing A Custom File Download Action Result For ASP.NET MVC

Another simplified example using FileResult as takepara suggested above.
Note I created a HelperController.cs class.
In your view...
#Html.ActionLink("Link Description", "Download", "Helper", new { fileName = "downloaded_file_name.ext", path = "root path location to your file" }, null)
Controller action...
public FileResult Download(string fileName, string path)
{
var webRootPath = Server.MapPath("~");
var documentationPath = Path.GetFullPath(Path.Combine(webRootPath, path));
var filePath = Path.GetFullPath(Path.Combine(documentationPath, fileName));
return File(filePath, System.Net.Mime.MediaTypeNames.Application.Octet, fileName);
}

Related

File Upload and Download Asp.Net Core Web API

I have to create a web API for file management which are file upload, download, delete in ASP.NET Core. The requirement is this that the file will be saved to the disk and the path, filename, UniqueId will be saved in the database. The Entity Model that I have created is this:
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
namespace FileManagerAPI.Model
{
public class FileDetail
{
public Guid Id { get; set; }
public DateTime? DateEntered { get; set; }
public bool? Deleted { get; set; }
public string DocumentName { get; set; }
public string DocId { get; set; }
public string DocType { get; set; }
public string DocUrl { get; set; }
}
}
Only selected types of files(pdf, png, jpg, jpeg) can be uploaded.
I have read a lot of documents but I couldn't make it work. Also, I have to save the files outside the project root directory. How can I implement this? Also I am using ASP.Net Core 3.1!
Here is what I have done to upload a file in my Controller.
namespace FileManagerAPI.Controllers
{
[Route("api/[controller]")]
[ApiController]
public class FilesController : ControllerBase
{
private readonly FileDbContext _context;
private readonly IHostingEnvironment _env;
public FilesController(FileDbContext context, IHostingEnvironment env)
{
_context = context;
_env = env;
}
[HttpPost]
public IActionResult Upload(IList<IFormFile>files)
{
//either you can pass the list of files in the method or you can initialize them inside the method like the commented line below
//var files = HttpContext.Request.Form.Files;
FileDetail fileDetail = new FileDetail();
foreach (var file in files)
{
var fileType = Path.GetExtension(file.FileName);
//var ext = file.;
if (fileType.ToLower() == ".pdf" || fileType.ToLower() == ".jpg" || fileType.ToLower() == ".png" || fileType.ToLower() == ".jpeg")
{
var filePath = _env.ContentRootPath;
var docName = Path.GetFileName(file.FileName);
if (file != null && file.Length > 0)
{
fileDetail.Id = Guid.NewGuid();
fileDetail.DocumentName = docName;
fileDetail.DocType = fileType;
fileDetail.DocUrl = Path.Combine(filePath, "Files", fileDetail.Id.ToString() + fileDetail.DocType);
using (var stream = new FileStream(fileDetail.DocUrl, FileMode.Create))
{
file.CopyToAsync(stream);
}
_context.Add(fileDetail);
_context.SaveChangesAsync();
}
else
{
return BadRequest();
}
}
}
return Ok();
}
[HttpGet]
public IActionResult Download(Guid id)
{
var fileDetail = _context.FileDetail
.Where(x => x.Id == id)
.FirstOrDefault();
if(fileDetail != null)
{
System.Net.Mime.ContentDisposition cd = new System.Net.Mime.ContentDisposition
{
FileName = fileDetail.DocumentName,
Inline = false
};
Response.Headers.Add("Content-Disposition", cd.ToString());
//get physical path
var path = _env.ContentRootPath;
var fileReadPath = Path.Combine(path, "Files", fileDetail.Id.ToString() + fileDetail.DocType);
var file = System.IO.File.OpenRead(fileReadPath);
return File(file, fileDetail.DocType);
}
else
{
return StatusCode(404);
}
}
}
For upload file - you should use interface IFormFile in your command and save Stream from that interface to eg. array of bytes. (Remeber - sending file should be send by HTTP Form Method). Before save you should check what is mime type and wheresome write information about file eg. in database.
For download file - you can use Method File in Controller. First arg of that method is Stream/Array of bytes/Physic path to file, second is mime/type.
To saving file outside Project Root can be sometimes probaly. Lot of external servers not shar that posibility. In my opinion should you save file in eg. Azure Blobs or simply in wwwroot in application.
If you are passing the file back to your controller using HttpPostedFileBase, you can adapt the following code to suit your needs.
string path = Path.Combine(Server.MapPath("~/Path/To/Desired/Folder"), file.FileName);
file.SaveAs(path);
file is a parameter of type HttpPostedFileBase, and is passed back to the controller via a HttpPost Method.
Just make sure that your program has the correct permissions to access the folder you desire.

Unable to delete file after HttpActionResult in WebAPI controller

I have an async controller method that does some heavy lifting when called, creates folders, zips files, and combines it into a final archive that is returned to the client via download. The problem is, I need to delete the files when finished but I'm getting an error during the delete:
The process cannot access the file 'Archive.zip' because it is being used by another process.
I understand the problem, but I can't figure out how to resolve it.
Here's some code snippets:
First, I created a custom filter:
[AttributeUsage(AttributeTargets.Method | AttributeTargets.Class)]
public class DeleteFileAttribute : ActionFilterAttribute
{
public override void OnActionExecuted(HttpActionExecutedContext filterContext)
{
// Delete file
HttpContext.Current.Response.Flush();
HttpContext.Current.Response.End();
var path = new DirectoryInfo(Library.Utilities.Files.MapPath("~/archives"));
foreach (var dir in path.GetDirectories())
{
dir.Delete(true);
}
base.OnActionExecuted(filterContext);
}
}
Here's my main controller entry point which I decorated with that custom filter:
[DeleteFile]
[HttpGet]
public async Task<IHttpActionResult> Download(string boardId, string boardName)
{
// I do a bunch of file related stuff, and then:
return new FileActionResult(boardName + ".zip", zipPath);
}
And here is my FileActionResult class:
public class FileActionResult : IHttpActionResult
{
public FileActionResult(string filename, string path)
{
Filename = filename;
Path = path;
}
public string Filename { get; }
public string Path { get; }
public Task<HttpResponseMessage> ExecuteAsync(CancellationToken cancellationToken)
{
HttpContext.Current.Response.Clear();
var response = new HttpResponseMessage {Content = new StreamContent(File.OpenRead(Path))};
response.Content.Headers.ContentType = new MediaTypeHeaderValue("application/x-zip-compressed");
response.Content.Headers.ContentDisposition = new ContentDispositionHeaderValue("attachment")
{
FileName = Filename
};
return Task.FromResult(response);
}
}
I assumed that by flushing and ending the response within the action filter prior to deleting the files would help, but it does not.
Try disposing of the stream content:
public Task<HttpResponseMessage> ExecuteAsync(CancellationToken cancellationToken)
{
HttpContext.Current.Response.Clear();
HttpResponseMessage response = null;
using (var stream = new File.OpenRead(path))
{
response = new HttpResponseMessage { Content = new StreamContent(stream)};
response.Content.Headers.ContentType = new MediaTypeHeaderValue("application/x-zip-compressed");
response.Content.Headers.ContentDisposition = new ContentDispositionHeaderValue("attachment")
{
FileName = Filename
};
}
return Task.FromResult(response);
}

Net core MVC - 'HttpCookie' does not exist in 'System.Web' [duplicate]

In ASP.NET MVC 5 I had the following extension:
public static ActionResult Alert(this ActionResult result, String text) {
HttpCookie cookie = new HttpCookie("alert") { Path = "/", Value = text };
HttpContext.Current.Response.Cookies.Add(cookie);
return result;
}
Basically I am adding a cookie with a text.
In ASP.NET Core I can't find a way to create the HttpCookie. Is this no longer possible?
Have you tried something like:
public static ActionResult Alert(this ActionResult result, Microsoft.AspNetCore.Http.HttpResponse response, string text)
{
response.Cookies.Append(
"alert",
text,
new Microsoft.AspNetCore.Http.CookieOptions()
{
Path = "/"
}
);
return result;
}
You may also have to pass the Response in your call to the extension method from the controller (or wherever you call it from). For example:
return Ok().Alert(Response, "Hi");
StackOverflow Reference
pika pika
public static ActionResult Alert(this ActionResult result, Microsoft.AspNetCore.Http.HttpResponse response, string text)
{
response.Cookies.Append(
"alert",
text,
new Microsoft.AspNetCore.Http.CookieOptions()
{
Path = "/"
}
);
return result;
}

How to change the content type of httppostedfilebase?

I am using HttpPostedFileBase to upload a file using ASP.NET MVC. I need to post this to a API call and i am getting a error
"StatusCode: 403, Reason Phrase: 'Forbidden', Version: 1.1, Content:
System.Net.Http.StreamContent".
This is because i am passing file content as "application/octet-stream", where as API call wants the content type as "application/vnd.qlik.sense.app".
Many posts in internet says that HttpPostedFileBase is a read only and we cannot change the content type. Can anybody let me know how we can change the content type of HttpPostedFileBase. Is this possible?
here is my code.
[HttpPost]
public ActionResult UploadFile(HttpPostedFile file)
{
if (file != null && file.ContentLength > 0)
{
var fileName = Path.GetFileNameWithoutExtension(file.FileName);
APIcall.Upload(fileName);
}
return RedirectToAction("Index");
}
Found this here how set HttpPostedFileBase ContentType value in runtime
public class MemoryPostedFile : HttpPostedFileBase
{
private readonly byte[] fileBytes;
public MemoryPostedFile(byte[] fileBytes, string fileName = null,string ContentType=null)
{
this.fileBytes = fileBytes;
this.FileName = fileName;
this.ContentType = ContentType;
this.InputStream = new MemoryStream(fileBytes);
}
public override int ContentLength => fileBytes.Length;
public override string FileName { get; }
public override string ContentType { get; }
public override Stream InputStream { get; }
}

checking to see if Stream inputStream File exists or not

I am changing a method that used to accept string for temp folder and string for file and changing it to a stream and i wanted some help how to check if file exists or not.
bool UploadFile(Stream inputStream, Stream inputFile);
This is what i originally had and i want to change so the parameters accepts a stream
bool UploadFile(string tempFolder, string fileName)
public bool UploadFile(string tempFolder, string fileName)
{
if (File.Exists(fileName))
{
testingUsage.Upload(tempFolder, fileName);
return testingUsage.Exists(tempFolder);
}
return false;
}
do i create two streams one for the file and one for location?
Assuming this is your Upload Action:
[HttpPost]
public ActionResult Upload()
{
try
{
if (Request.Files.Count > 0)
{
string tempFolder = "...";
var file = Request.Files[0];
if(UploadFile(tempFolder, file))
{
// Return a View to show a message that file was successfully uploaded...
return View();
}
}
}
catch (Exception e)
{
// Handle the exception here...
}
}
Your Method can be something like this:
private bool UploadFile(string tempFolder, HttpPostedFileBase file)
{
var path = Path.Combine(tempFolder, file.FileName);
// if the file does not exist, save it.
if (!File.Exists(path))
{
file.SaveAs(path);
return true;
}
return false;
}

Categories

Resources