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.