在ASP中返回一个文件到查看/下载。NET MVC

我遇到了一个问题,将存储在数据库中的文件发送回ASP中的用户。净MVC。我想要的是一个列出两个链接的视图,一个用于查看文件并让发送到浏览器的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};

有什么建议吗?


< >强更新: 这个问题似乎引起了很多人的共鸣,所以我想我应该更新一下。下面由Oskar添加的关于国际字符的接受答案的警告是完全有效的,由于使用ContentDisposition类,我已经击中了它几次。我已经更新了我的实现来解决这个问题。下面的代码来自我在ASP中这个问题的最新版本。NET Core(全框架)应用程序,它应该与旧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
}
460602 次浏览
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

我相信这个答案更清晰,(基于 https://stackoverflow.com/a/3007668/550975) < / p >
    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));
}

FileVirtualPath—> Research\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("Content-Disposition", cd.ToString());可能会导致浏览器无法渲染文件,如果你的响应已经包含了一个“Content-Disposition”头。在这种情况下,你可能想使用:

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

视图文件(例如txt):

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

下载文件(例如txt):

return File("~/TextFileInRootDir.txt", MediaTypeNames.Text.Plain, "TextFile.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);
}

Action方法需要返回FileResult与流,字节[],或文件的虚拟路径。您还需要知道正在下载的文件的内容类型。下面是一个示例(快速/肮脏)实用方法。视频链接示例 如何使用asp.net core下载文件 < / p >

[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控制器来处理文件下载部分,您就无法使用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);
}
}

接下来,确保您的应用程序启动(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的领域里得到这个看似简单的问题的适当答案…!