FileResult kullanarak Asp.Net MVC herhangi bir tür dosya indirmek?


230

Bana kullanıcıların Asp.Net MVC uygulamasından dosya indirmelerine izin vermek için FileResult kullanmam gerektiğini önerdim. Ancak bulabildiğim tek örnek her zaman resim dosyalarıyla (içerik türünü belirten image / jpeg) ilgili.

Ama dosya türünü bilemezsem ne olur? Kullanıcıların sitemin dosyasından hemen hemen her dosyayı indirmesini istiyorum.

Bunu yapmak için tek bir yöntem okumuştum ( kod için önceki bir gönderiye bakın ), aslında bir şey hariç, iyi çalışıyor: Farklı Kaydet iletişim kutusunda gelen dosyanın adı alt çizgi ile dosya yolundan bitiştiriliyor ( folder_folder_file.ext). Ayrıca, insanlar BinaryContentResult bulduğum bu özel sınıfı kullanmak yerine FileResult döndürmek gerektiğini düşünüyorum görünüyor.

Herkes MVC böyle bir indirme yapmanın "doğru" yolunu biliyor musunuz?

EDIT: Cevabı aldım (aşağıda), ancak başka biri ilgilenirse tam çalışma kodunu göndermem gerektiğini düşündüm:

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
Yaptığınız şey oldukça tehlikeli. Kullanıcıların sunucunuzdan yürütme kullanıcısının erişebileceği herhangi bir dosyayı indirmesine izin veriyorsunuz.
Paul Fleming

1
Doğru - dosya yolunu kaldırmak ve eylem sonucunun gövdesinde çivilemek biraz daha güvenli olacaktır. En azından bu şekilde sadece belirli bir klasöre erişimleri olur.
shubniggurath

2
Bunun gibi tehlikeli olabilecek boşluklar bulmanızı sağlayan herhangi bir araç var mı?
David

Ben o kadar ayarlanan içerik tipine uygun olduğunu bulmak Response.ContentType = MimeMapping.GetMimeMapping(filePath);dan, stackoverflow.com/a/22231074/4573839
yu Yang Jian

Müşteri tarafında ne kullanıyorsunuz?
FrenkyB

Yanıtlar:


427

Sadece genel sekizli akış MIME türünü belirtebilirsiniz:

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
Tamam, bunu deneyebilirim, ama byte [] dizisine ne oluyor?
Anders

3
Boş ver, sanırım çözdüm. Dosya adını (tam yol) bir FileStream içine ve sonra bir bayt dizisine okudum ve sonra bir cazibe gibi çalıştı! Teşekkürler!
Anders

5
Bu, tüm dosyayı dışarı akıtmak için belleğe yükler; büyük dosyalar için, bu bir domuz. Çok daha iyi bir çözüm, önce dosyayı belleğe yüklemek zorunda olmayan çözümdür.
HBlackorby

13
Bu cevap neredeyse beş yaşında olduğu için, evet. Bunu çok büyük dosyalara sunmak için yapıyorsanız, yapmayın. Mümkünse, ayrı bir statik dosya sunucusu kullanın, böylece uygulama iş parçacıklarınızı veya 2010'dan beri MVC'ye eklenen dosyaları sunmak için birçok yeni teknikten birini bağlamayın. Bu, MIME türü bilinmiyorsa kullanılacak doğru MIME türünü gösterir. . ReadAllBytesyıllar sonra bir düzenlemeye eklendi. Bu neden benim en çok onaylanan ikinci cevabım? Oh iyi.
Ian Henry

10
Bu hatayı alıyorum:non-invocable member "File" cannot be used like a method.
A-Sharabiani

106

MVC çerçevesi bunu doğal olarak desteklemektedir. System.Web.MVC.Controller.File kontrol ile bir dosya geri yöntemler sağlar adı / akımı / dizi .

Örneğin, dosyaya sanal bir yol kullanarak aşağıdakileri yapabilirsiniz.

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

36

.NET Framework 4.5 kullanıyorsanız, dosyanızın MIME türünü almak için MimeMapping.GetMimeMapping (dize DosyaAdı) kullanın. Benim eylemimde böyle kullandım.

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

Bu Mime haritalama olsun güzel, ama çalışma zamanında dosya türünün ne olduğunu bulmak için bir süreç değil mi?
Mohammed Noureldin

@MohammedNoureldin "figüran" değil, dosya uzantılarına veya bunun gibi bir şeye dayanan basit bir eşleme tablosu var. Sunucu bunu tüm statik dosyalar için yapar, yavaş değildir.
Al Kepp

13

Phil Haack'in güzel bir makalesi var Özel Dosya İndirme Eylem Sonuç sınıfı oluşturduğu . Yalnızca dosyanın sanal yolunu ve kaydedilecek adı belirtmeniz gerekir.

Bir kez kullandım ve işte kodum.

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

Örneğimde, dosyaların fiziksel yolunu saklıyordum, bu yüzden sanal bir yola dönüştürmek için hatırlayamadığım bir yerde bu yardımcı yöntemi kullandım

        private string GetVirtualPath(string physicalPath)
        {
            string rootpath = Server.MapPath("~/");

            physicalPath = physicalPath.Replace(rootpath, "");
            physicalPath = physicalPath.Replace("\\", "/");

            return "~/" + physicalPath;
        }

İşte Phill Haack'in makalesinden alındığı gibi tam sınıf

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
Doğru, evet, bu makaleyi de gördüm, ancak kullandığım makaleyle aynı şeyi yapıyor gibi görünüyor (önceki yazımın referansına bakın) ve sayfanın üst kısmında geçici çözümün olmaması gerektiğini söylüyor ' t Artık gerekli çünkü: "YENİ GÜNCELLEME: ASP.NET MVC artık kutuda bir tane içerdiğinden artık bu özel ActionResult'a gerek yok." Ancak ne yazık ki, bunun nasıl kullanılacağı hakkında başka bir şey söylemiyor.
Anders

@ManafAbuRous, kodu yakından okursanız, sanal yolu fiziksel yola ( Server.MapPath(this.VirtualPath)) dönüştürdüğünü göreceksiniz, bu yüzden bunu doğrudan değişiklik yapmadan tüketmek biraz naiftir. PhysicalPathSonunda gerekli olan ve depoladığınız şey olduğu göz önüne alındığında kabul eden bir alternatif üretmelisiniz . Fiziksel yolun ve göreceli yolun aynı olacağı (kök hariç) olduğu varsayımıyla bu çok daha güvenli olacaktır. Veri dosyaları genellikle App_Data saklanır. Buna göreli yol olarak erişilemez.
Paul Fleming

GetVirtualPath harika .... çok yararlı. teşekkür ederim!
Zvi Redler

6

Ian Henry'ye teşekkürler !

MS SQL Server'dan dosya almanız gerekiyorsa çözüm burada.

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

Nerede AppModel olan EntityFrameworkmodeli ve MyFiles hediyeler tablo veritabanınızda. Filedata olduğu varbinary(MAX)içinde MyFiles masaya.


2

bu basit sadece dosya adı ile directoryPath fiziksel yol vermek

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

Bu yöntemi çağıran müşteri tarafı ne olacak? Kaydet iletişim kutusu olarak göstermek istiyorsanız şunu söyleyelim mi?
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 (dosyaAdı)) İçeriği döndürür ("dosya adı mevcut değil");

        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 dosyayı kapatmalı (veya kullanarak açmalıdır). Daha sonra dosyayı bayta dönüştürdükten sonra silebilirsiniz - indirme işlemi bu bayt arabelleğinde yapılır.

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

İndirme yönteminizde ...

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

3
Lütfen asla, asla tüm dosyaları belleğe böyle üretime
yükleyin
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.