I\'m encountering a problem sending files stored in a database back to the user in ASP.NET MVC. What I want is a view listing two links, one to view the file and let the mim
If, like me, you've come to this topic via Razor components as you're learning Blazor, then you'll find you need to think a little more outside of the box to solve this problem. It's a bit of a minefield if (also like me) Blazor is your first forray into the MVC-type world, as the documentation isn't as helpful for such 'menial' tasks.
So, at the time of writing, you cannot achieve this using vanilla Blazor/Razor without embedding an MVC controller to handle the file download part an example of which is as below:
using Microsoft.AspNetCore.Mvc;
using Microsoft.Net.Http.Headers;
[Route("api/[controller]")]
[ApiController]
public class FileHandlingController : ControllerBase
{
[HttpGet]
public FileContentResult Download(int attachmentId)
{
TaskAttachment taskFile = null;
if (attachmentId > 0)
{
// taskFile = <your code to get the file>
// which assumes it's an object with relevant properties as required below
if (taskFile != null)
{
var cd = new System.Net.Http.Headers.ContentDispositionHeaderValue("attachment")
{
FileNameStar = taskFile.Filename
};
Response.Headers.Add(HeaderNames.ContentDisposition, cd.ToString());
}
}
return new FileContentResult(taskFile?.FileData, taskFile?.FileContentType);
}
}
Next, make sure your application startup (Startup.cs) is configured to correctly use MVC and has the following line present (add it if not):
services.AddMvc();
.. and then finally modify your component to link to the controller, for example (iterative based example using a custom class):
<tbody>
@foreach (var attachment in yourAttachments)
{
<tr>
<td><a href="api/FileHandling?attachmentId=@attachment.TaskAttachmentId" target="_blank">@attachment.Filename</a> </td>
<td>@attachment.CreatedUser</td>
<td>@attachment.Created?.ToString("dd MMM yyyy")</td>
<td><ul><li class="oi oi-circle-x delete-attachment"></li></ul></td>
</tr>
}
</tbody>
Hopefully this helps anyone who struggled (like me!) to get an appropriate answer to this seemingly simple question in the realms of Blazor…!
public ActionResult Download()
{
var document = ...
var cd = new System.Net.Mime.ContentDisposition
{
// for example foo.bak
FileName = document.FileName,
// always prompt the user for downloading, set to true if you want
// the browser to try to show the file inline
Inline = false,
};
Response.AppendHeader("Content-Disposition", cd.ToString());
return File(document.Data, document.ContentType);
}
NOTE: This example code above fails to properly account for international characters in the filename. See RFC6266 for the relevant standardization. I believe recent versions of ASP.Net MVC's File()
method and the ContentDispositionHeaderValue
class properly accounts for this. - Oskar 2016-02-25
I believe this answer is cleaner, (based on https://stackoverflow.com/a/3007668/550975)
public ActionResult GetAttachment(long id)
{
FileAttachment attachment;
using (var db = new TheContext())
{
attachment = db.FileAttachments.FirstOrDefault(x => x.Id == id);
}
return File(attachment.FileData, "application/force-download", Path.GetFileName(attachment.FileName));
}
Action method needs to return FileResult with either a stream, byte[], or virtual path of the file. You will also need to know the content-type of the file being downloaded. Here is a sample (quick/dirty) utility method. Sample video link How to download files using asp.net core
[Route("api/[controller]")]
public class DownloadController : Controller
{
[HttpGet]
public async Task<IActionResult> Download()
{
var path = @"C:\Vetrivel\winforms.png";
var memory = new MemoryStream();
using (var stream = new FileStream(path, FileMode.Open))
{
await stream.CopyToAsync(memory);
}
memory.Position = 0;
var ext = Path.GetExtension(path).ToLowerInvariant();
return File(memory, GetMimeTypes()[ext], Path.GetFileName(path));
}
private Dictionary<string, string> GetMimeTypes()
{
return new Dictionary<string, string>
{
{".txt", "text/plain"},
{".pdf", "application/pdf"},
{".doc", "application/vnd.ms-word"},
{".docx", "application/vnd.ms-word"},
{".png", "image/png"},
{".jpg", "image/jpeg"},
...
};
}
}
Below code worked for me for getting a pdf file from an API service and response it out to the browser - hope it helps;
public async Task<FileResult> PrintPdfStatements(string fileName)
{
var fileContent = await GetFileStreamAsync(fileName);
var fileContentBytes = ((MemoryStream)fileContent).ToArray();
return File(fileContentBytes, System.Net.Mime.MediaTypeNames.Application.Pdf);
}
Darin Dimitrov's answer is correct. Just an addition:
Response.AppendHeader("Content-Disposition", cd.ToString());
may cause the browser to fail rendering the file if your response already contains a "Content-Disposition" header. In that case, you may want to use:
Response.Headers.Add("Content-Disposition", cd.ToString());