Рекомендуемый способ создания ActionResult с расширением файла
Мне нужно создать 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", как способ сделать это.
Ответы
Ответ 1
Я думаю, что ваш ответ должен содержать заголовок 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);
}
}
}
И используйте его в своем Action вместо ContentResult:
return new MyCsvResult {
Content = Emails.Aggregate((a,b) => a + Environment.NewLine + b)
/* Optional
* , ContentEncoding = ""
* , Name = "DoNotEmailList.csv"
*/
};
Ответ 2
Я использовал действие 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")
}
Он добавит вам заголовок содержимого.
Ответ 3
Вот как я делаю что-то подобное. Я рассматриваю его как загрузку:
var disposition = String.Format(
"attachment;filename=\"{0}.csv\"", this.Model.Name);
Response.AddHeader("content-disposition", disposition);
Это должно отображаться в браузере в виде загрузки файла с заданным именем файла.
Я не могу думать о причине, почему твоя работа не будет работать.
Ответ 4
Ответ, который вы приняли, достаточно хорош, но он сохраняет содержимое вывода в памяти, когда он выводит его. Что делать, если файл, который он создает, довольно большой? Например, когда вы выгружаете содержимое таблицы SQL. У вашего приложения может быть нехватка памяти. В этом случае вы хотите использовать FileStreamResult. Один из способов подачи данных в поток мог бы использовать pipe, как я описал здесь