我已经建议我应该使用 FileResult 来允许用户从我的 Asp.Net MVC 应用程序下载文件。但我能找到的唯一例子总是与图像文件有关(指定内容类型图像/jpeg)。
但是如果我不知道文件类型怎么办?我希望用户能够从我网站的文件区域下载几乎任何文件。
我已经阅读了一种执行此操作的方法(参见代码的 previous post),它实际上工作正常,除了一件事:在“另存为”对话框中出现的文件的名称是从文件路径与下划线连接起来的(folder_folder_file.ext)。此外,似乎人们认为我应该返回一个 FileResult,而不是使用我找到的 BinaryContentResult 这个自定义类。
任何人都知道在 MVC 中进行此类下载的“正确”方式吗?
编辑:我得到了答案(如下),但只是认为如果其他人感兴趣,我应该发布完整的工作代码:
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;
}
Response.ContentType = MimeMapping.GetMimeMapping(filePath);
很方便,从 stackoverflow.com/a/22231074/4573839
您可以只指定通用八位字节流 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 框架本身就支持这一点。 System.Web.MVC.Controller.File 控制器提供通过 name/stream/array 返回文件的方法。
例如,使用文件的虚拟路径,您可以执行以下操作。
return File(virtualFilePath, System.Net.Mime.MediaTypeNames.Application.Octet, Path.GetFileName(virtualFilePath));
如果您使用的是 .NET Framework 4.5,则使用 MimeMapping.GetMimeMapping(string FileName) 获取文件的 MIME-Type。这就是我在行动中使用它的方式。
return File(Path.Combine(@"c:\path", fileFromDB.FileNameOnDisk), MimeMapping.GetMimeMapping(fileFromDB.FileName), fileFromDB.FileName);
Phil Haack 有一个很好的article,他在其中创建了自定义文件下载操作结果类。您只需要指定文件的虚拟路径和要保存的名称。
我用过一次,这是我的代码。
[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 };
}
在我的示例中,我存储了文件的物理路径,所以我使用了这个帮助方法——我在某个我不记得的地方找到了——将它转换为虚拟路径
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);
}
}
Server.MapPath(this.VirtualPath)
),因此直接使用它而不进行更改有点幼稚。您应该生成一个接受 PhysicalPath
的替代,因为这是最终需要的并且是您存储的。这会更安全,因为您假设物理路径和相对路径是相同的(不包括根)。数据文件通常存储的是App_Data。这不能作为相对路径访问。
感谢伊恩亨利!
如果您需要从 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 在您的数据库中呈现 table。 FileData 是 MyFiles 表中的 varbinary(MAX)
。
它很简单,只需在 directoryPath 中提供您的物理路径和文件名
public FilePathResult GetFileFromDisk(string fileName)
{
return File(directoryPath, "multipart/form-data", 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);
}
if (string.IsNullOrWhiteSpace(fileName)) return Content("文件名不存在");
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);
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));
ReadAllBytes
是多年后在一次修改中添加的。为什么这是我第二个最赞成的答案?那好吧。non-invocable member "File" cannot be used like a method.