Returning a file to View/Download in ASP.NET MVC

前端 未结 9 1556
刺人心
刺人心 2020-11-21 22:41

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

相关标签:
9条回答
  • 2020-11-21 22:43

    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…!

    0 讨论(0)
  • 2020-11-21 22:44
    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

    0 讨论(0)
  • 2020-11-21 22:46

    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));
        }
    
    0 讨论(0)
  • 2020-11-21 22:48

    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"},
                ...
            };
        }
    }
    
    0 讨论(0)
  • 2020-11-21 22:49

    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);
        }
    
    0 讨论(0)
  • 2020-11-21 22:51

    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());
    
    0 讨论(0)
提交回复
热议问题