Рекомендуемый способ создания ActionResult с расширением файла - PullRequest
19 голосов
/ 13 июня 2009

Мне нужно создать ActionResult в приложении ASP.NET MVC с типом файла .csv.

Я предоставлю список адресов электронной почты «не звонить» моим маркетинговым партнерам, и я хочу, чтобы в типе файла у него было расширение .csv. Затем он автоматически откроется в Excel.

 http://www.example.com/mailinglist/donotemaillist.csv?password=12334

Я успешно сделал это следующим образом, но я хочу убедиться, что это абсолютно лучший и рекомендуемый способ сделать это.

    [ActionName("DoNotEmailList.csv")]
    public ContentResult DoNotEmailList(string username, string password)
    {
            return new ContentResult()
            {
                Content = Emails.Aggregate((a,b)=>a+Environment.NewLine + b), 
                ContentType = "text/csv"
            };
    }

Этот Actionmethod будет отлично отвечать на вышеуказанную ссылку.

Мне просто интересно, есть ли вероятность неожиданного конфликта с расширением файла, подобного этому, с любой другой версией IIS, любым видом фильтра ISAPI или чем-то еще, о чем я не могу думать сейчас.

Мне нужно быть на 100% уверенным, потому что я буду предоставлять это внешним партнерам и не хочу, чтобы потом передумал. Я действительно не вижу каких-либо проблем, но, может быть, есть что-то неясное - или другой, более похожий на MVC, способ сделать это.

Ответы [ 4 ]

29 голосов
/ 19 октября 2011

Я использовал действие FileContentResult, чтобы сделать что-то подобное.

public FileContentResult DoNotEmailList(string username, string password)
{
        string csv = Emails.Aggregate((a,b)=>a+Environment.NewLine + b);
        byte[] csvBytes = ASCIIEncoding.ASCII.GetBytes( csv );
        return File(csvBytes, "text/csv", "DoNotEmailList.csv");
}

Он добавит заголовок размещения контента для вас.

21 голосов
/ 13 июня 2009

Я думаю, что ваш ответ ДОЛЖЕН содержать заголовок «Content-Disposition» в этом случае. Создайте собственный ActionResult, как это:

public class MyCsvResult : ActionResult {

    public string Content {
        get;
        set;
    }

    public Encoding ContentEncoding {
        get;
        set;
    }

    public string Name {
        get;
        set;
    }

    public override void ExecuteResult(ControllerContext context) {
        if (context == null) {
            throw new ArgumentNullException("context");
        }

        HttpResponseBase response = context.HttpContext.Response;

        response.ContentType = "text/csv";

        if (ContentEncoding != null) {
            response.ContentEncoding = ContentEncoding;
        }

        var fileName = "file.csv";

        if(!String.IsNullOrEmpty(Name)) {
            fileName = Name.Contains('.') ? Name : Name + ".csv";
        }

        response.AddHeader("Content-Disposition",
            String.Format("attachment; filename={0}", fileName));

        if (Content != null) {
            response.Write(Content);
        }
    }
}

И используйте его в своем действии вместо ContentResult:

return new MyCsvResult {
    Content = Emails.Aggregate((a,b) => a + Environment.NewLine + b)
    /* Optional
     * , ContentEncoding = ""
     * , Name = "DoNotEmailList.csv"
     */
};
6 голосов
/ 13 июня 2009

Вот как я делаю нечто подобное. Я рассматриваю это как загрузку:

var disposition = String.Format(
  "attachment;filename=\"{0}.csv\"", this.Model.Name);
Response.AddHeader("content-disposition", disposition);

Это должно отображаться в браузере как загрузка файла с заданным именем файла.

Я не могу вспомнить причину, по которой ваша не сработает.

0 голосов
/ 05 июня 2015

Ответ, который вы приняли, достаточно хорош, но он сохраняет содержимое вывода в памяти при выводе. Что если генерируемый файл достаточно велик? Например, при сбросе содержимого таблицы SQL. Вашему приложению может не хватить памяти. В этом случае вы хотите использовать FileStreamResult. Одним из способов подачи данных в поток может быть использование канала , как я описал здесь

...