2010-08-30 4 views
184

Я предположил, что должен использовать FileResult, чтобы пользователи могли загружать файлы из моего приложения ASP.NET MVC. Но единственные примеры, которые я могу найти, всегда связаны с файлами изображений (с указанием типа содержимого image/jpeg).Загрузить файл любого типа в Asp.Net MVC с помощью FileResult?

Но что, если я не могу узнать тип файла? Я хочу, чтобы пользователи могли загружать практически любой файл из файловой области моего сайта.

Я прочитал один из способов сделать это (см. Код previous post для кода), который на самом деле отлично работает, за исключением одного: имя файла, которое появляется в диалоговом окне «Сохранить как», конкатенируется из пути к файлу с символами подчеркивания (folder_folder_file.ext). Кроме того, кажется, люди думают, что я должен вернуть FileResult вместо использования этого пользовательского класса, который я нашел BinaryContentResult.

Кто-нибудь знает «правильный» способ сделать такую ​​загрузку в MVC?

EDIT: я получил ответ (ниже), но просто подумал, что я должен опубликовать полный рабочий код, если кто-то заинтересован:

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

То, что вы делаете, это довольно опасно. Вы в значительной степени позволяете пользователям загружать любой файл с вашего сервера, к которому может обратиться пользователь-пользователь. –

+1

True - удаление пути к файлу, и прибивание его в тело actionresult было бы несколько безопаснее. По крайней мере, они имеют доступ только к определенной папке. – shubniggurath

+1

Существуют ли какие-либо инструменты, которые позволят вам найти потенциально опасные лазейки, такие как этот? – David

ответ

331

Вы можете просто указать общий тип октет-поток 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

+0

Спасибо за публикацию обновленного решения! – shubniggurath

7

Фил Хаак имеет приятный article, где он создал Custome File Загрузить класс Action Result. Вам нужно указать виртуальный путь файла и имя, которое нужно сохранить как.

Я использовал его один раз, и вот мой код.

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

В моем примере я хранил физический путь к файлу, так как я использовал этот вспомогательный метод -Вот я нашел где-то я не могу remember-, чтобы преобразовать его в виртуальный путь

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

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

      return "~/" + physicalPath; 
     } 

Вот полный класс, как взят из статьи Phill Хаака

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

Правильно, да, я тоже видел эту статью, но это похоже на то, что я использовал (см. Ссылку на мой предыдущий пост), и он сам говорит в верхней части страницы, что обходное решение должно «больше не нужно, потому что:« NEW UPDATE: больше не требуется этот пользовательский ActionResult, потому что ASP.NET MVC теперь включает в себя один из них ». Но, к сожалению, он больше ничего не говорит о том, как это использовать. – Anders

+0

@ManafAbuRous, если вы внимательно прочитаете код, вы увидите, что он фактически преобразует виртуальный путь в физический путь ('Server.MapPath (this.VirtualPath)'), так что потребление этого напрямую без изменений является чуть-чуть наивным. Вы должны создать альтернативу, которая принимает «PhysicalPath», поскольку это то, что в конечном итоге требуется, и что вы храните. Это было бы намного безопаснее, поскольку вы сделали предположение, что физический путь и относительный путь будут одинаковыми (исключая корень). Файлы данных часто хранятся в App_Data. Это недоступно как относительный путь. –

81

Основа MVC поддерживает это изначально. Контроллер System.Web.MVC.Controller.File предоставляет методы для возврата файла на name/stream/array.

Например, используя виртуальный путь к файлу, вы можете сделать следующее.

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

То, что мне было нужно и очень короткое, спасибо большое! –

+0

Отлично работает, спасибо за очень ясный пример. –

+6

+1: Это должен быть правильный ответ, поскольку фактическая проблема касается целых файлов, и это лучший способ загрузить их, которые я видел. –

29

Если вы используете .NET Framework 4.5, то вы используете использовать MimeMapping.GetMimeMapping (строка имя_файла), чтобы получить MIME-тип для вашего файла. Вот как я использовал его в своих действиях.

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

Что получить Mime mapping приятно, но разве это не процесс heave, чтобы выяснить, какой тип файла во время выполнения? –

-2

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

Пожалуйста, никогда не загружайте целые файлы в память в производство, как это – makhdumi

3

Благодаря Ян Генри!

В случае, если вам нужно , чтобы получить файл из 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 таблица.

1

его просто дайте свой физический путь в DirectoryPath с именем файла

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