Como definir o download do nome do arquivo na API da Web do ASP.NET


140

Na minha classe ApiController, tenho o seguinte método para baixar um arquivo criado pelo servidor.

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

Tudo está funcionando perfeitamente, exceto que o nome do arquivo de download padrão é o seu ID, para que o usuário precise digitar seu próprio nome de arquivo ao salvar como caixa de diálogo a cada vez. Existe alguma maneira de definir um nome de arquivo padrão no código acima?


1
você pode compartilhar o código que você usou para chamar esse método?
Yasser Shaikh

@Yasser - este é um método de controlador de API da Web - provavelmente está sendo chamado por meio de solicitações HTTP que chegam ao IIS e as analisa e encontra rotas e API da Web que chamam o método (e, é claro, também é chamado por testes).
Dave Rael

o que está acontecendo dentro do GetMyForm ()? Convertendo os arquivos em fluxo de bytes?
21418 MSIslam

@MSIslam Mais ou menos. A função obtém informações do formulário do usuário e cria um arquivo antes de converter para o fluxo resultante.
Tae-Sung Shin

Respostas:


288

Você precisa definir o Content-Dispositioncabeçalho no 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
Para qualquer pessoa curiosa sobre o tipo de disposição "anexo", a lista completa de tipos de disposição está em iana.org/assignments/cont-disp/cont-disp.xhtml
sfuqua

1
Você tem outra resposta para baixar um arquivo aqui. Importa se você usa System.Net.Mime.ContentDispositionou ContentDispositionHeaderValue? Um é mais atual e mais preferido que o outro?
Luminous

@Luminous uma resposta é para ActionResult, é paraHttpResponseMessage
weston

@weston sua resposta não responde à minha pergunta.
Luminous

4
@Luminous "Isso importa" e "Um é mais atual e mais preferido que o outro?" Não e não. Um é para MVC ActionResulte outro é para WebApi HttpResponseMessage.
weston 21/05

27

EDIT: Como mencionado em um comentário, minha resposta não conta para caracteres que precisam ser escapados como a ;. Você deve usar a resposta aceita por Darin, se o nome do arquivo puder conter um ponto-e-vírgula.

Adicione um Response.AddHeader para definir o nome do arquivo

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

Apenas mude FILE_NAME para o nome do arquivo.


2
Isso me ajudou a resolver um problema semelhante ao que fez a pergunta. No meu caso, também achei útil alterar "anexo" para "inline" para que o IE8 me desse a opção de abrir sempre o tipo de arquivo em questão.
22413 Scott

2
Não cobre escapar. E se o nome do arquivo incluir um ;ou algo mais com um significado especial?
Sam

Sam, quando escrevi essa resposta há 3 anos, não sabia que minha resposta era necessária para lidar com a fuga. Obrigado por me indicar isso, fiz uma edição na minha resposta, explicando que minha resposta não conta como escapar. Mas mantendo a minha resposta a mesma, pois parecia ter ajudado as pessoas.
KingPancake

8

Se você deseja garantir que o nome do arquivo seja codificado corretamente, mas também evite o WebApi HttpResponseMessage, use o seguinte:

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

Você pode usar ContentDisposition ou ContentDispositionHeaderValue. Chamar o ToString em uma instância de qualquer um deles fará a codificação dos nomes dos arquivos para você.


6

Eu acho que isso pode ser útil para você.

Response.AddHeader("Content-Disposition", "attachment; filename=" + fileName)

4
Não cobre escapar. E se o nome do arquivo incluir um ;ou algo mais com um significado especial?
Sam

3

Você precisa adicionar o cabeçalho de disposição do conteúdo à resposta:

 response.StatusCode = HttpStatusCode.OK;
 response.Content = new StreamContent(result);
 response.AppendHeader("content-disposition", "attachment; filename=" + fileName);
 return response;

3
Não cobre escapar. E se o nome do arquivo incluir um ;ou algo mais com um significado especial?
Sam

3

Se você estiver usando o ASP.NET Core MVC, as respostas acima são ligeiramente alteradas ...

No meu método de ação (que retorna async Task<JsonResult>), adiciono a linha (em qualquer lugar antes da declaração de retorno):

Response.Headers.Add("Content-Disposition", $"attachment; filename={myFileName}");

Não cobre escapar. E se o nome do arquivo incluir um; ou algo mais com um significado especial?
KoalaBear

2

Isso deve fazer:

Response.AddHeader("Content-Disposition", "attachment;filename="+ YourFilename)

2
Não cobre escapar. E se o nome do arquivo incluir um ;ou algo mais com um significado especial?
Sam

0

Nota: A última linha é obrigatória.

Se não especificarmos Access-Control-Expose-Headers , não obteremos o nome do arquivo na interface do usuário.

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

Considerando as respostas anteriores, é necessário ter cuidado com os caracteres globalizados.

Suponha que o nome do arquivo seja: " Esdrújula prenda ñame - güena.jpg "

Resultado bruto para o download: "Esdrújula prenda Amém - gena.jpg" [Feio]

Resultado HtmlEncode para download: "Esdr" - jula prenda & _241; ame - g & _252; ena.jpg "[Feio]

Resultado do UrlEncode para fazer o download: " Esdrújula + prenda + nome + - + güena.jpg " [OK]

Em seguida, você quase sempre precisa usar o UrlEncode sobre o nome do arquivo . Além disso, se você definir o cabeçalho de disposição do conteúdo como sequência direta, precisará garantir aspas entre aspas para evitar problemas de compatibilidade do navegador.

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

ou com auxílio de classe:

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

O System.Net.Mime. A classe ContentDisposition cuida de aspas.

Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.