Как установить имя загружаемого файла в веб-API ASP.NET

142

В моем классе ApiController у меня есть следующий метод для загрузки файла, созданного сервером.

public HttpResponseMessage Get(int id)
{
    try
    {
        string dir = HttpContext.Current.Server.MapPath("~"); //location of the template file
        Stream file = new MemoryStream();
        Stream result = _service.GetMyForm(id, dir, file);
        if (result == null)
        {
            return Request.CreateResponse(HttpStatusCode.NotFound);
        }
        result.Position = 0;
        HttpResponseMessage response = new HttpResponseMessage();
        response.StatusCode = HttpStatusCode.OK;
        response.Content = new StreamContent(result);
        return response;
    }
    catch (IOException)
    {
        return Request.CreateResponse(HttpStatusCode.InternalServerError);
    }
}

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

Тэ-Сун Шин
источник
1
вы можете поделиться кодом, который вы использовали для вызова этого метода?
Ясир Шейх
@Yasser - это метод контроллера веб-API - он, вероятно, вызывается через HTTP-запросы, поступающие в IIS и анализирующие их, а также находящие маршруты и веб-API, вызывающие метод (и, конечно же, он также вызывается тестами).
Дэйв Раэль
что происходит внутри GetMyForm ()? Преобразование файлов в поток байтов?
MSIslam
@MSIslam Вроде. Функция получает информацию из формы пользователя и создает файл перед преобразованием в результирующий поток.
Tae-Sung Shin

Ответы:

294

Вам нужно установить Content-Dispositionзаголовок на HttpResponseMessage:

HttpResponseMessage response = new HttpResponseMessage();
response.StatusCode = HttpStatusCode.OK;
response.Content = new StreamContent(result);
response.Content.Headers.ContentDisposition = new ContentDispositionHeaderValue("attachment")
{
    FileName = "foo.txt"
};
Дарин Димитров
источник
21
Для всех, кто интересуется типом диспозиции «привязка», полный список типов диспозиции находится на iana.org/assignments/cont-disp/cont-disp.xhtml
sfuqua
1
У вас есть еще один ответ на загрузку файла здесь. Имеет значение, используете ли вы System.Net.Mime.ContentDispositionили ContentDispositionHeaderValue? Один более актуален и предпочтительнее другого?
Luminous
@Luminous: один ответ за ActionResult, другой заHttpResponseMessage
Уэстон
@weston, ваш ответ не отвечает на мой вопрос.
Luminous
4
@Luminous "Имеет ли это значение" и "Один более актуален и предпочтительнее другого?" Нет и нет. Один предназначен для MVC ActionResult, а другой - для WebApi HttpResponseMessage.
Уэстон
27

РЕДАКТИРОВАТЬ: Как упоминалось в комментарии, мой ответ не учитывает символы, которые необходимо экранировать, например ;. Вы должны использовать принятый ответ Дарина, если имя вашего файла может содержать точку с запятой.

Добавьте Response.AddHeader, чтобы указать имя файла

Response.AddHeader("Content-Disposition", "attachment; filename=*FILE_NAME*");

Просто измените FILE_NAME на имя файла.

KingPancake
источник
2
Это оказалось полезным для меня в решении проблемы, аналогичной задающей вопрос. В моем случае я также счел полезным изменить «вложение» на «встроенный», чтобы IE8 давал мне возможность всегда открывать файл нужного типа.
Скотт
2
Не покрывает побег. Что, если имя файла включает в себя ;или что-то еще со специальным значением?
Sam
Сэм, в то время, когда я писал этот ответ 3 года назад, я не понимал, что мой ответ необходим для побега. Спасибо, что указали мне на это, я отредактировал свой ответ, объясняя, что мой ответ не учитывает побег. Но я сохраняю свой ответ прежним, поскольку он, похоже, помог людям.
KingPancake
9

Если вы хотите убедиться, что имя файла правильно закодировано, но также не хотите использовать WebApi HttpResponseMessage, вы можете использовать следующее:

Response.AddHeader("Content-Disposition", new System.Net.Mime.ContentDisposition("attachment") { FileName = "foo.txt" }.ToString());

Вы можете использовать ContentDisposition или ContentDispositionHeaderValue. Вызов ToString в экземпляре любого из них выполнит кодирование имен файлов за вас.

Sorenhk
источник
6

Думаю, это может быть вам полезно.

Response.AddHeader("Content-Disposition", "attachment; filename=" + fileName)
Ярек
источник
4
Не покрывает побег. Что, если имя файла включает в себя ;или что-то еще со специальным значением?
Sam
3

Вам нужно добавить в ответ заголовок content-disposition:

 response.StatusCode = HttpStatusCode.OK;
 response.Content = new StreamContent(result);
 response.AppendHeader("content-disposition", "attachment; filename=" + fileName);
 return response;
Карл Раймонд
источник
3
Не покрывает побег. Что, если имя файла включает в себя ;или что-то еще со специальным значением?
Sam
3

Если вы используете ASP.NET Core MVC, приведенные выше ответы будут немного изменены ...

В моем методе действия (который возвращает async Task<JsonResult>) я добавляю строку (в любом месте перед оператором возврата):

Response.Headers.Add("Content-Disposition", $"attachment; filename={myFileName}");
Питер
источник
Не покрывает побег. Что, если имя файла включает в себя; или что-то еще с особым смыслом?
KoalaBear
2

Это должно делать:

Response.AddHeader("Content-Disposition", "attachment;filename="+ YourFilename)
Туказ
источник
2
Не покрывает побег. Что, если имя файла включает в себя ;или что-то еще со специальным значением?
Сэм
0

Примечание: последняя строка является обязательной.

Если мы не указали Access-Control-Expose-Headers , мы не получим имя файла в пользовательском интерфейсе.

FileInfo file = new FileInfo(FILEPATH);

HttpResponseMessage response = new HttpResponseMessage(HttpStatusCode.OK);
response.Content.Headers.ContentDisposition = new ContentDispositionHeaderValue("attachment")
{
    FileName = file.Name
};
response.Content.Headers.Add("Access-Control-Expose-Headers", "Content-Disposition");
Чандан Ю.С.
источник
0

Учитывая предыдущие ответы, необходимо быть осторожным с глобализированными персонажами.

Предположим, файл имеет имя: " Esdrújula prenda ñame - güena.jpg "

Необработанный результат для загрузки: "Esdrújula prenda à ± ame - güena.jpg" [Ugly]

HtmlEncode результат для загрузки: "Esdr & _250; jula prenda & _241; ame - g & _252; ena.jpg" [Ugly]

Результат URLEncode для загрузки: " Esdrújula + prenda + ñame + - + güena.jpg " [OK]

Затем вам почти всегда нужно использовать UrlEncode над именем файла . Более того, если вы установите заголовок content-disposition как прямую строку, вам необходимо заключить его в кавычки, чтобы избежать проблем с совместимостью браузера.

Response.AddHeader("Content-Disposition", $"attachment; filename=\"{HttpUtility.UrlEncode(YourFilename)}\"");

или с помощью класса:

var cd = new ContentDisposition("attachment") { FileName = HttpUtility.UrlEncode(resultFileName) };
Response.AddHeader("Content-Disposition", cd.ToString());

Файл System.Net.Mime. Класс ContentDisposition заботится о кавычках.

Asereware
источник