ChatGPT解决这个技术问题 Extra ChatGPT

使用 FileResult 在 Asp.Net MVC 中下载任何类型的文件?

我已经建议我应该使用 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;
}
你的所作所为相当危险。您几乎允许用户从您的服务器下载执行用户可以访问的任何文件。
是的 - 删除文件路径,并将其固定在 actionresult 的主体中会更安全一些。至少这样他们只能访问某个文件夹。
是否有任何工具可以让您找到像这样的潜在危险漏洞?
我发现将 content-type 设置为 Response.ContentType = MimeMapping.GetMimeMapping(filePath); 很方便,从 stackoverflow.com/a/22231074/4573839
你在客户端使用什么?

T
Tieson T.

您可以只指定通用八位字节流 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);
}

好的,我可以试试,但是 byte[] 数组中有什么?
没关系,我想我想通了。我将文件名(完整路径)读入 FileStream,然后读入字节数组,然后它就像一个魅力!谢谢!
这会将整个文件加载到内存中,只是为了将其流出;对于大文件,这是一头猪。一个更好的解决方案是下面的解决方案,它不必先将文件加载到内存中。
因为这个答案已经快五年了,是的。如果您这样做是为了提供非常大的文件,请不要这样做。如果可能,请使用单独的静态文件服务器,这样您就不会占用应用程序线程,或者使用自 2010 年以来添加到 MVC 的许多提供文件服务的新技术之一。这仅显示了当 MIME 类型未知时要使用的正确 MIME 类型. ReadAllBytes 是多年后在一次修改中添加的。为什么这是我第二个最赞成的答案?那好吧。
收到此错误:non-invocable member "File" cannot be used like a method.
J
Jonathan

MVC 框架本身就支持这一点。 System.Web.MVC.Controller.File 控制器提供通过 name/stream/array 返回文件的方法。

例如,使用文件的虚拟路径,您可以执行以下操作。

return File(virtualFilePath, System.Net.Mime.MediaTypeNames.Application.Octet,  Path.GetFileName(virtualFilePath));

S
Salman Hasrat Khan

如果您使用的是 .NET Framework 4.5,则使用 MimeMapping.GetMimeMapping(string FileName) 获取文件的 MIME-Type。这就是我在行动中使用它的方式。

return File(Path.Combine(@"c:\path", fileFromDB.FileNameOnDisk), MimeMapping.GetMimeMapping(fileFromDB.FileName), fileFromDB.FileName);

获取 Mime 映射很好,但是在运行时确定文件的类型不是一个繁重的过程吗?
@MohammedNoureldin 它不是“计算”它,有一个基于文件扩展名或类似内容的简单映射表。服务器对所有静态文件执行此操作,它并不慢。
B
Bernard Vander Beken

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);
    }
}

是的,是的,我也看到了那篇文章,但它似乎和我使用的文章做的一样(参见我之前帖子的参考),他在页面顶部说自己应该解决方法'不再需要,因为:“新更新:不再需要此自定义 ActionResult,因为 ASP.NET MVC 现在在框中包含一个。”但不幸的是,他没有说明如何使用它。
@ManafAbuRous,如果您仔细阅读代码,您会发现它实际上将虚拟路径转换为物理路径(Server.MapPath(this.VirtualPath)),因此直接使用它而不进行更改有点幼稚。您应该生成一个接受 PhysicalPath 的替代,因为这是最终需要的并且是您存储的。这会更安全,因为您假设物理路径和相对路径是相同的(不包括根)。数据文件通常存储的是App_Data。这不能作为相对路径访问。
GetVirtualPath 很棒....非常有用。谢谢你!
N
NoWar

感谢伊恩亨利!

如果您需要从 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;
        }

其中 AppModelEntityFramework 模型,MyFiles 在您的数据库中呈现 tableFileDataMyFiles 表中的 varbinary(MAX)


b
benka

它很简单,只需在 directoryPath 中提供您的物理路径和文件名

public FilePathResult GetFileFromDisk(string fileName)
{
    return File(directoryPath, "multipart/form-data", fileName);
}

客户端呢,调用这个方法呢?可以说是否要显示另存为对话框?
h
hussein zakizadeh
   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);
        }

C
Caio Augusto

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);

C
CDichter

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));

请永远不要像这样将整个文件加载到内存中进行生产

关注公众号,不定期副业成功案例分享
关注公众号

不定期副业成功案例分享

领先一步获取最新的外包任务吗?

立即订阅