C# 在ASP.NET MVC中返回要查看/下载的文件
在ASP.NET MVC中将存储在数据库中的文件发送回用户时遇到问题。我想要的是一个列出两个链接的视图,一个用于查看文件并让发送到浏览器的mimetype确定应该如何处理它,另一个用于强制下载 如果我选择查看一个名为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的文件,我希望只打开该文件。但是我还想保留一个下载链接,这样无论文件
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>