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?

Tae-Sung Shin
fonte
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"
};
Darin Dimitrov
fonte
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.

KingPancake
fonte
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ê.

sorenhk
fonte
6

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

Response.AddHeader("Content-Disposition", "attachment; filename=" + fileName)
Jarek
fonte
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;
Carl Raymond
fonte
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}");
Peter
fonte
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)
tucaz
fonte
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");
Chandan YS
fonte
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.

Asereware
fonte