返回文件以在 MVC ASP.NET 查看/下载

本文关键字:NET 查看 下载 ASP MVC 文件 返回 | 更新日期: 2023-09-27 17:54:12

我在将存储在数据库中的文件发送回 MVC 中的用户时遇到问题 ASP.NET。我想要的是一个列出两个链接的视图,一个用于查看文件并让发送到浏览器的 mimetype 确定应如何处理它,另一个用于强制下载。

如果我选择查看名为 SomeRandomFile.bak 的文件,并且浏览器没有关联的程序来打开这种类型的文件,那么我对它默认为下载行为没有问题。但是,如果我选择查看名为SomeRandomFile.pdfSomeRandomFile.jpg的文件,我希望该文件只是打开。但是我也想在侧面保留一个下载链接,以便无论文件类型如何,我都可以强制下载提示。这有意义吗?

我已经尝试过FileStreamResult它适用于大多数文件,默认情况下它的构造函数不接受文件名,因此根据 URL 为未知文件分配文件名(它不知道根据内容类型给出的扩展名(。如果我通过指定文件名来强制使用文件名,我将失去浏览器直接打开文件的能力,并收到下载提示。还有其他人遇到过这种情况吗?

这些是我到目前为止尝试过的例子。

//Gives me a download prompt.
return File(document.Data, document.ContentType, document.Name);

//Opens if it is a known extension type, downloads otherwise (download has bogus name and missing extension)
return new FileStreamResult(new MemoryStream(document.Data), document.ContentType);

//Gives me a download prompt (lose the ability to open by default if known type)
return new FileStreamResult(new MemoryStream(document.Data), document.ContentType) {FileDownloadName = document.Name};

有什么建议吗?


更新:这个问题似乎引起了很多人的共鸣,所以我想我会发布更新。奥斯卡在下面关于国际字符的已接受答案上添加的警告是完全有效的,由于使用了ContentDisposition类,我已经点击了几次。此后,我更新了我的实现以解决此问题。虽然下面的代码来自我最近在 ASP.NET Core(完整框架(应用程序中对此问题的化身,但由于我使用的是 System.Net.Http.Headers.ContentDispositionHeaderValue 类,因此它也应该在较旧的 MVC 应用程序中进行最小的更改。

using System.Net.Http.Headers;
public IActionResult Download()
{
    Document document = ... //Obtain document from database context
    //"attachment" means always prompt the user to download
    //"inline" means let the browser try and handle it
    var cd = new ContentDispositionHeaderValue("attachment")
    {
        FileNameStar = document.FileName
    };
    Response.Headers.Add(HeaderNames.ContentDisposition, cd.ToString());
    return File(document.Data, document.ContentType);
}
// an entity class for the document in my database 
public class Document
{
    public string FileName { get; set; }
    public string ContentType { get; set; }
    public byte[] Data { get; set; }
    //Other properties left out for brevity
}

返回文件以在 MVC ASP.NET 查看/下载

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);
}

注意:上面的此示例代码无法正确说明文件名中的国际字符。有关相关标准化,请参阅RFC6266。我相信 ASP.Net MVC的File()方法和ContentDispositionHeaderValue类的最新版本正确地解释了这一点。- 奥斯卡 2016-02-25

由于"document"变量上没有类型提示,我对接受的答案有问题: var document = ... 所以我发布了对我有用的东西作为替代方案,以防其他人遇到麻烦。

public ActionResult DownloadFile()
{
    string filename = "File.pdf";
    string filepath = AppDomain.CurrentDomain.BaseDirectory + "/Path/To/File/" + filename;
    byte[] filedata = System.IO.File.ReadAllBytes(filepath);
    string contentType = MimeMapping.GetMimeMapping(filepath);
    var cd = new System.Net.Mime.ContentDisposition
    {
        FileName = filename,
        Inline = true,
    };
    Response.AppendHeader("Content-Disposition", cd.ToString());
    return File(filedata, contentType);
}

查看文件(例如 txt(:

return File("~/TextFileInRootDir.txt", MediaTypeNames.Text.Plain);

下载文件(例如 txt(:

return File("~/TextFileInRootDir.txt", MediaTypeNames.Text.Plain, "TextFile.txt");

注意:要下载文件,我们应该传递文件下载名称参数

Darin Dimitrov的回答是正确的。只是一个补充:

Response.AppendHeader("Content-Disposition", cd.ToString()); 如果您的响应已包含"内容处置"标头,则可能会导致浏览器无法呈现文件。在这种情况下,您可能需要使用:

Response.Headers.Add("Content-Disposition", cd.ToString());
我相信

这个答案更干净,(基于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));
    }

下面的代码为我工作,用于从 API 服务获取 pdf 文件并将其响应到浏览器 - 希望它有所帮助;

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);
    }

FileVirtualPath --> Research''Global Office Review.pdf

public virtual ActionResult GetFile()
{
    return File(FileVirtualPath, "application/force-download", Path.GetFileName(FileVirtualPath));
}

操作方法需要返回带有文件的流、字节 [] 或虚拟路径的 FileResult。您还需要知道正在下载的文件的内容类型。这是一个示例(快速/脏(实用程序方法。示例视频链接如何使用 asp.net 核心下载文件

[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"},
            ...
        };
    }
}

如果你像我一样,在学习 Blazor 时通过 Razor 组件了解了这个主题,那么你会发现你需要跳出框框思考来解决这个问题。 如果(也像我一样(Blazor 是你第一次进入 MVC 类型的世界,那就有点雷区了,因为文档对这种"琐碎"的任务没有帮助。

因此,在撰写本文时,如果不嵌入 MVC 控制器来处理文件下载部分,则无法使用原版 Blazor/Razor 实现此目的,示例如下:

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);
    }
}

接下来,确保您的应用程序启动 (Startup.cs( 配置为正确使用 MVC,并且存在以下行(如果没有,请添加它(:

        services.AddMvc();

.. 然后最后修改您的组件以链接到控制器,例如(使用自定义类的基于迭代的示例(:

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

希望这能帮助任何挣扎的人(像我一样!(在 Blazor 领域为这个看似简单的问题找到适当的答案......!