FileResult를 사용하여 Asp.Net MVC에서 모든 유형의 파일을 다운로드 하시겠습니까?


228

사용자가 내 Asp.Net MVC 응용 프로그램에서 파일을 다운로드 할 수 있도록 FileResult를 사용해야한다고 제안했습니다. 그러나 내가 찾을 수있는 유일한 예는 항상 이미지 파일 (콘텐츠 유형 이미지 / JPEG 지정)과 관련이 있습니다.

그러나 파일 형식을 알 수 없으면 어떻게합니까? 사용자가 내 사이트의 파일 영역에서 거의 모든 파일을 다운로드 할 수 있기를 바랍니다.

이 작업을 수행하는 한 가지 방법 ( 이전 코드 참조)을 읽었 지만 실제로는 한 가지 경우를 제외하고는 잘 작동합니다. 다른 이름으로 저장 대화 상자에 나타나는 파일 이름은 파일 경로에서 밑줄로 연결됩니다 ( folder_folder_file.ext). 또한 사람들은 BinaryContentResult를 찾은이 사용자 정의 클래스를 사용하는 대신 FileResult를 반환해야한다고 생각합니다.

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

12
당신이하는 일은 오히려 위험합니다. 사용자는 서버에서 실행중인 사용자가 액세스 할 수있는 모든 파일을 다운로드 할 수 있습니다.
— Paul Fleming

1
사실-파일 경로를 제거하고 동작 결과 본문에 고정시키는 것이 다소 안전합니다. 적어도 그런 식으로 그들은 특정 폴더에만 액세스 할 수 있습니다.
— shubniggurath

2
이와 같은 위험한 허점을 찾을 수있는 도구가 있습니까?
— David

stackoverflow.com/a/22231074/4573839Response.ContentType = MimeMapping.GetMimeMapping(filePath); 에서 content-type을 설정하는 것이 편리하다는 것을 알았습니다.
— yu yang Jian

클라이언트 측에서 무엇을 사용하고 있습니까?
— FrenkyB

답변:


425

일반적인 옥텟 스트림 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);
}

4
좋아, 나는 그것을 시도 할 수 있지만 byte [] 배열로 들어가는 것은 무엇입니까?
— Anders

3
신경 쓰지 마, 내가 알아 낸 것 같아 FileStream으로 파일 이름 (전체 경로)을 읽은 다음 바이트 배열로 읽은 다음 매력처럼 작동했습니다! 감사!
— Anders

5
이것은 전체 파일을 메모리로로드하여 스트리밍합니다. 큰 파일의 경우 이것은 돼지입니다. 훨씬 더 나은 솔루션은 파일을 메모리에 먼저로드 할 필요가없는 아래 솔루션입니다.
— HBlackorby

13
이 대답은 거의 5 살이 기 때문에 그렇습니다. 매우 큰 파일을 제공하기 위해이 작업을 수행하는 경우에는하지 마십시오. 가능하면 별도의 정적 파일 서버를 사용하여 응용 프로그램 스레드를 묶지 않거나 2010 년 이후 MVC에 추가 된 파일을 제공하는 많은 새로운 기술 중 하나를 사용하십시오. MIME 유형을 알 수없는 경우 사용할 올바른 MIME 유형 만 표시합니다. . ReadAllBytes몇 년 후 편집에 추가되었습니다. 왜 이것이 나의 두 번째로 큰 대답입니까? 오 잘
— Ian Henry

10
이 오류가 발생했습니다 :non-invocable member "File" cannot be used like a method.
— A-Sharabiani

105

MVC 프레임 워크는이를 기본적으로 지원합니다. System.Web.MVC.Controller.File의 제어기에 의해 파일을 반환하는 방법을 제공 이름 / 스트림 / 어레이 .

예를 들어 파일의 가상 경로를 사용하면 다음을 수행 할 수 있습니다.

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

36

.NET Framework 4.5를 사용하는 경우 MimeMapping.GetMimeMapping (문자열 FileName)을 사용하여 파일의 MIME 유형을 가져옵니다. 이것이 내가 행동에 사용한 방법입니다.

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

Mime 매핑은 훌륭하지만 런타임에 파일 유형이 무엇인지 알아내는 데 많은 시간이 걸리지 않습니까?
— Mohammed Noureldin

@MohammedNoureldin 그것은 "피겨"가 아니며, 파일 확장자 또는 이와 유사한 것을 기반으로하는 간단한 매핑 테이블이 있습니다. 서버는 모든 정적 파일에 대해 수행하지만 느리지 않습니다.
— Al Kepp

13

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

내 예제에서 나는 파일의 물리적 경로를 저장하고 있었기 때문에이 도우미 방법을 사용하여 가상 경로로 변환 할 수없는 곳을 찾았습니다.

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

1
맞아, 그래, 그 기사도 보았지만, 내가 사용한 기사와 똑같은 일을하는 것 같다 (이전 게시물 참조 참조). "새로운 업데이트 : ASP.NET MVC에 상자가 하나 포함되어 있으므로 더 이상이 사용자 정의 ActionResult가 필요하지 않습니다." 그러나 불행히도 그는 이것이 어떻게 사용되는지에 대해 다른 말을하지 않습니다.
— Anders

@ManafAbuRous, 코드를 자세히 읽으면 실제로 가상 경로를 실제 경로 ( Server.MapPath(this.VirtualPath)) 로 변환 하므로 변경하지 않고 직접 소비하는 것은 다소 순진합니다. 당신 PhysicalPath은 결국 그것이 요구되고 당신이 저장하고있는 것을 받아들이는 대체물을 생산해야합니다 . 물리적 경로와 상대 경로가 같다고 가정했을 때 (루트 제외) 훨씬 안전합니다. 데이터 파일은 종종 App_Data입니다. 상대 경로로 액세스 할 수 없습니다.
— Paul Fleming

GetVirtualPath는 훌륭합니다 ... 매우 유용합니다. 감사합니다!
— Zvi Redler

6

이안 헨리 감사합니다 !

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의 테이블.


2

그것의 간단한 파일 이름으로 directoryPath에 실제 경로를 제공하십시오.

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

이 메소드를 호출하는 클라이언트 측은 어떻습니까? 다른 이름으로 저장 대화 상자를 표시하고 싶습니까?
— FrenkyB

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

-1

if (string.IsNullOrWhiteSpace (fileName)) 반환 내용 ( "filename not present");

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

-4

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

2
절대로 전체 파일을 메모리에로드하지 마십시오.
— makhdumi
당사 사이트를 사용함과 동시에 당사의 쿠키 정책과 개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.