C# 在ASP.NET MVC中返回要查看/下载的文件

C# 在ASP.NET MVC中返回要查看/下载的文件,c#,asp.net-mvc,asp.net-mvc-3,download,http-headers,C#,Asp.net Mvc,Asp.net Mvc 3,Download,Http Headers,在ASP.NET MVC中将存储在数据库中的文件发送回用户时遇到问题。我想要的是一个列出两个链接的视图,一个用于查看文件并让发送到浏览器的mimetype确定应该如何处理它,另一个用于强制下载 如果我选择查看一个名为SomeRandomFile.bak的文件,而浏览器没有相关的程序来打开这种类型的文件,那么默认为下载行为就没有问题。但是,如果我选择查看名为SomeRandomFile.pdf或SomeRandomFile.jpg的文件,我希望只打开该文件。但是我还想保留一个下载链接,这样无论文件

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

如果我选择查看一个名为
SomeRandomFile.bak
的文件,而浏览器没有相关的程序来打开这种类型的文件,那么默认为下载行为就没有问题。但是,如果我选择查看名为
SomeRandomFile.pdf
SomeRandomFile.jpg
的文件,我希望只打开该文件。但是我还想保留一个下载链接,这样无论文件类型如何,我都可以强制进行下载提示。这有意义吗

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

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

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

有什么建议吗


更新: 这个问题似乎引起了很多人的共鸣,所以我想我应该发布一个更新。下面由Oskar添加的关于国际字符的公认答案上的警告是完全有效的,由于使用了
ContentDisposition
类,我已经点击了几次。我已经更新了我的实现来解决这个问题。虽然下面的代码来自我最近在ASP.NET核心(完整框架)应用程序中遇到的这个问题,但它应该在旧的MVC应用程序中进行最小的更改,因为我使用的是
System.NET.Http.Headers.ContentDispositionHeaderValue

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
}

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

我相信这个答案更干净,(基于 )

FileVirtualPath-->研究\Global Office Review.pdf

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

我在接受答案时遇到了问题,因为在“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);
}
这是正确的。只是一个补充:

Response.AppendHeader(“内容处置”,cd.ToString())可能会导致浏览器无法呈现文件。在这种情况下,您可能需要使用:

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

要查看文件(例如txt),请执行以下操作:

下载文件(例如txt):


注意:要下载文件,我们应该传递下面的fileDownloadName参数

代码,用于从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);
    }
public异步任务PrintPdfStatements(字符串文件名)
{
var fileContent=await GetFileStreamAsync(文件名);
var fileContentBytes=((MemoryStream)fileContent.ToArray();
返回文件(fileContentBytes,System.Net.Mime.MediaTypeNames.Application.Pdf);
}

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

[路由(“api/[控制器]”)]
公共类下载控制器:控制器
{
[HttpGet]
公共异步任务下载()
{
var path=@“C:\Vetrivel\winforms.png”;
var memory=newmemoryStream();
使用(var stream=newfilestream(路径,FileMode.Open))
{
等待流。CopyToAsync(内存);
}
记忆位置=0;
var ext=Path.GetExtension(Path.ToLowerInvariant();
返回文件(内存,GetMimeTypes()[ext],Path.GetFileName(Path));
}
私有字典GetMimeTypes()
{
返回新词典
{
{.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控制器来处理文件下载部分,您就无法使用vanilla 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);
    }
}
。。然后最后修改组件以链接到控制器,例如(使用自定义类的基于迭代的示例):


@foreach(附件中的var附件)
{
@附件.CreatedUser
@附件.Created?.ToString(“dd-MMM-yyyy”)
}

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

如果我没有记错的话,只要文件名没有空格,它就可以不加引号(我通过HttpUtility.UrlEncode()运行了我的文件来实现这一点)
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);
}
Response.Headers.Add("Content-Disposition", cd.ToString());
return File("~/TextFileInRootDir.txt", MediaTypeNames.Text.Plain);
return File("~/TextFileInRootDir.txt", MediaTypeNames.Text.Plain, "TextFile.txt");
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);
    }
[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"},
            ...
        };
    }
}
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);
    }
}
        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>