C# 使用FileResult在Asp.NETMVC中下载任何类型的文件?
有人建议我应该使用FileResult来允许用户从我的Asp.NETMVC应用程序下载文件。但我能找到的唯一例子总是与图像文件有关(指定内容类型image/jpeg) 但是如果我不知道文件类型呢?我希望用户能够从我网站的文件区下载几乎任何文件 我读过这样做的一种方法(代码见a),实际上效果很好,除了一件事:在“另存为”对话框中出现的文件名是用下划线(folder\u folder\u file.ext)从文件路径连接而来的。另外,人们似乎认为我应该返回一个FileResult,而不是使用我发现的BinaryContentResult自定义类 有人知道在MVC中进行这种下载的“正确”方法吗 编辑: 我得到了答案(如下),但我想如果其他人感兴趣,我应该发布完整的工作代码:C# 使用FileResult在Asp.NETMVC中下载任何类型的文件?,c#,asp.net-mvc-2,C#,Asp.net Mvc 2,有人建议我应该使用FileResult来允许用户从我的Asp.NETMVC应用程序下载文件。但我能找到的唯一例子总是与图像文件有关(指定内容类型image/jpeg) 但是如果我不知道文件类型呢?我希望用户能够从我网站的文件区下载几乎任何文件 我读过这样做的一种方法(代码见a),实际上效果很好,除了一件事:在“另存为”对话框中出现的文件名是用下划线(folder\u folder\u file.ext)从文件路径连接而来的。另外,人们似乎认为我应该返回一个FileResult,而不是使用我发现的
public ActionResult Download(string filePath, string fileName)
{
string fullName = Path.Combine(GetBaseDir(), filePath, fileName);
byte[] fileBytes = GetFile(fullName);
return File(
fileBytes, System.Net.Mime.MediaTypeNames.Application.Octet, fileName);
}
byte[] GetFile(string s)
{
System.IO.FileStream fs = System.IO.File.OpenRead(s);
byte[] data = new byte[fs.Length];
int br = fs.Read(data, 0, data.Length);
if (br != fs.Length)
throw new System.IO.IOException(s);
return data;
}
Phil Haack创建了一个自定义文件下载操作结果类。您只需要指定文件的虚拟路径和要另存为的名称
我用过一次,这是我的代码
[AcceptVerbs(HttpVerbs.Get)]
public ActionResult Download(int fileID)
{
Data.LinqToSql.File file = _fileService.GetByID(fileID);
return new DownloadResult { VirtualPath = GetVirtualPath(file.Path),
FileDownloadName = file.Name };
}
在我的示例中,我存储了文件的物理路径,所以我使用了这个helper方法(我在不记得的地方找到了这个方法)将其转换为虚拟路径
private string GetVirtualPath(string physicalPath)
{
string rootpath = Server.MapPath("~/");
physicalPath = physicalPath.Replace(rootpath, "");
physicalPath = physicalPath.Replace("\\", "/");
return "~/" + physicalPath;
}
下面是从Phill Haack的文章中摘录的完整课程
public class DownloadResult : ActionResult {
public DownloadResult() {}
public DownloadResult(string virtualPath) {
this.VirtualPath = virtualPath;
}
public string VirtualPath {
get;
set;
}
public string FileDownloadName {
get;
set;
}
public override void ExecuteResult(ControllerContext context) {
if (!String.IsNullOrEmpty(FileDownloadName)) {
context.HttpContext.Response.AddHeader("content-disposition",
"attachment; filename=" + this.FileDownloadName)
}
string filePath = context.HttpContext.Server.MapPath(this.VirtualPath);
context.HttpContext.Response.TransmitFile(filePath);
}
}
您只需指定通用八位字节流MIME类型:
public FileResult Download()
{
byte[] fileBytes = System.IO.File.ReadAllBytes(@"c:\folder\myfile.ext");
string fileName = "myfile.ext";
return File(fileBytes, System.Net.Mime.MediaTypeNames.Application.Octet, fileName);
}
MVC框架本机支持这一点。控制器提供了通过//返回文件的方法 例如,使用文件的虚拟路径可以执行以下操作
return File(virtualFilePath, System.Net.Mime.MediaTypeNames.Application.Octet, Path.GetFileName(virtualFilePath));
如果您使用的是.NET Framework 4.5,则可以使用(字符串文件名)获取文件的MIME类型。这就是我在行动中使用它的方式
return File(Path.Combine(@"c:\path", fileFromDB.FileNameOnDisk), MimeMapping.GetMimeMapping(fileFromDB.FileName), fileFromDB.FileName);
GetFile应该关闭该文件(或在使用中打开它)。然后,您可以在转换为字节后删除该文件——下载将在该字节缓冲区上完成
byte[] GetFile(string s)
{
byte[] data;
using (System.IO.FileStream fs = System.IO.File.OpenRead(s))
{
data = new byte[fs.Length];
int br = fs.Read(data, 0, data.Length);
if (br != fs.Length)
throw new System.IO.IOException(s);
}
return data;
}
所以在你的下载方法中
byte[] fileBytes = GetFile(file);
// delete the file after conversion to bytes
System.IO.File.Delete(file);
// have the file download dialog only display the base name of the file return File(fileBytes, System.Net.Mime.MediaTypeNames.Application.Octet, Path.GetFileName(file));
多亏了伊恩·亨利 如果您需要从MS SQL Server获取文件,这里有一个解决方案
public FileResult DownloadDocument(string id)
{
if (!string.IsNullOrEmpty(id))
{
try
{
var fileId = Guid.Parse(id);
var myFile = AppModel.MyFiles.SingleOrDefault(x => x.Id == fileId);
if (myFile != null)
{
byte[] fileBytes = myFile.FileData;
return File(fileBytes, System.Net.Mime.MediaTypeNames.Application.Octet, myFile.FileName);
}
}
catch
{
}
}
return null;
}
其中,AppModel是EntityFramework
模型,MyFiles显示数据库中的表。
FileData是
varbinary(MAX)
在MyFiles表中。很简单,只需在directoryPath中用文件名指定物理路径即可
public FilePathResult GetFileFromDisk(string fileName)
{
return File(directoryPath, "multipart/form-data", fileName);
}
if(string.IsNullOrWhiteSpace(文件名))
返回内容(“文件名不存在”)
是的,是的,我也看到了那篇文章,但它似乎与我使用的那篇文章做了类似的事情(参见我之前文章的参考资料),他在页面顶部说,不应该再需要解决方法了,因为:“新更新:不再需要此自定义ActionResult,因为ASP.NET MVC现在在框中包含一个。”但不幸的是,他没有说明如何使用它。@ManafAbuRous,如果仔细阅读代码,您将看到它实际上将虚拟路径转换为物理路径(
Server.MapPath(this.VirtualPath))
)因此,直接使用它而不进行更改有点幼稚。您应该生成一个接受物理路径的备用路径,因为这是最终需要的,也是您正在存储的路径。这会更安全,因为您假设物理路径和相对路径相同(不包括根路径).Data文件通常存储为App_数据。这不能作为相对路径访问。GetVirtualPath非常有用。谢谢!好的,我可以试试,但字节[]数组中有什么内容?没关系,我想我已经找到了。我读取了文件名(完整路径)进入一个文件流,然后进入一个字节数组,然后它像一个符咒一样工作!谢谢!这将整个文件加载到内存中只是为了流出来;对于大文件,这是一个麻烦。下面的解决方案更好,它不必先将文件加载到内存中。因为这个答案已经有将近五年的历史了,是的。如果你这样做是为了服务非常大的文件,请不要。如果可能,请使用单独的静态文件服务器,这样您就不会占用应用程序线程,或者使用自2010年以来添加到MVC的许多新技术中的一种。这只是显示了当MIME类型未知时要使用的正确MIME类型。ReadAllBytes
是几年后在编辑中添加的。为什么这是我的第二个最上浮的答案?哦,好吧。得到这个错误:noninvocable成员”文件“不能像方法一样使用。
你所做的事情相当危险。您基本上允许用户从您的服务器下载执行用户可以访问的任何文件。正确-删除文件路径,并将其固定在actionresult主体中会更安全一些。至少这样,他们只能访问某个文件夹。是否有任何工具可以让您找到像这样的潜在危险漏洞?我发现将内容类型设置为Response.ContentType=MimeMapping.GetMimeMapping(filePath)比较方便
,您在客户端使用的是什么?请不要,永远不要像这样将整个文件加载到内存到产品中。获取Mime映射很好,但要确定运行时的文件类型不是一个很难的过程吗?@MohammedNoureldin它不是“弄清楚”它,有一个基于文件扩展名或类似内容的简单映射表。服务器对所有静态文件执行此操作,速度并不慢。客户端调用此方法如何?如果要显示“另存为”对话框?
var path = Path.Combine(your path, your filename);
var stream = new FileStream(path, FileMode.Open);
return File(stream, System.Net.Mime.MediaTypeNames.Application.Octet, fileName);
public ActionResult Download()
{
var document = //Obtain document from database context
var cd = new System.Net.Mime.ContentDisposition
{
FileName = document.FileName,
Inline = false,
};
Response.AppendHeader("Content-Disposition", cd.ToString());
return File(document.Data, document.ContentType);
}