Jak ustawić nazwę pobierania pliku w ASP.NET Web API

140

W mojej klasie ApiController mam następującą metodę pobierania pliku utworzonego przez serwer.

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

Wszystko działa idealnie, z wyjątkiem tego, że domyślną nazwą pliku do pobrania jest jego identyfikator, więc użytkownik może być zmuszony do wpisania własnej nazwy pliku przy zapisywaniu jako okno dialogowe za każdym razem. Czy istnieje sposób, aby ustawić domyślną nazwę pliku w powyższym kodzie?

Tae-Sung Shin
źródło
1
czy możesz udostępnić kod, którego użyłeś do wywołania tej metody?
Yasser Shaikh
@Yasser - jest to metoda kontrolera interfejsu API sieci Web - prawdopodobnie jest wywoływana przez żądania HTTP przychodzące do IIS i analizuje je oraz znajduje trasy i internetowy interfejs API wywołując metodę (i oczywiście jest również wywoływana przez testy).
Dave Rael
co się dzieje w GetMyForm ()? Konwertowanie plików na strumień bajtów?
MSIslam
@MSIslam W pewnym sensie. Funkcja pobiera informacje z formularza użytkownika i tworzy plik przed konwersją na wynikowy strumień.
Tae-Sung Shin

Odpowiedzi:

289

Musisz ustawić Content-Dispositionnagłówek na 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
źródło
21
Dla każdego, kto jest ciekawy typu dyspozycji „załącznik”, pełna lista typów dyspozycji jest dostępna pod adresem iana.org/ assignments
cont
1
Masz inną odpowiedź dotyczącą pobierania pliku tutaj. Czy to ma znaczenie, czy używasz, System.Net.Mime.ContentDispositionczy ContentDispositionHeaderValue? Czy jeden jest bardziej aktualny i preferowany niż drugi?
Luminous
@Luminous jedna odpowiedź jest za ActionResult, jedna zaHttpResponseMessage
weston
@weston twoja odpowiedź nie odpowiada na moje pytanie.
Luminous
4
@Luminous „Czy to ma znaczenie” i „Czy jeden jest bardziej aktualny i preferowany od drugiego?” Nie i nie. Jeden jest dla MVC ActionResult, a drugi dla WebApi HttpResponseMessage.
weston
27

EDYCJA: Jak wspomniano w komentarzu, w mojej odpowiedzi nie uwzględniono znaków, którym należy uciec, takich jak ;. Powinieneś użyć zaakceptowanej odpowiedzi, której udzielił Darin, jeśli nazwa twojego pliku może zawierać średnik.

Dodaj Response.AddHeader, aby ustawić nazwę pliku

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

Po prostu zmień FILE_NAME na nazwę pliku.

KingPancake
źródło
2
Okazało się to pomocne w rozwiązaniu problemu podobnego do osoby zadającej pytanie. W moim przypadku przydała mi się również zmiana „załącznika” na „w tekście”, tak aby w IE8 zawsze można było otwierać dany typ pliku.
Scott
2
Nie obejmuje ucieczki. Co się stanie, jeśli nazwa pliku zawiera znak ;lub coś innego o specjalnym znaczeniu?
Sam
Sam, w czasie, gdy pisałem tę odpowiedź 3 lata temu, nie zdawałem sobie sprawy, że moja odpowiedź musi poradzić sobie z ucieczką. Dziękuję za wskazanie mi tego, zredagowałem moją odpowiedź, wyjaśniając, że moja odpowiedź nie stanowi ucieczki. Ale moja odpowiedź pozostała taka sama, ponieważ wydawało się, że pomogła ludziom.
KingPancake
8

Jeśli chcesz upewnić się, że nazwa pliku jest poprawnie zakodowana, ale jednocześnie uniknąć WebApi HttpResponseMessage, możesz użyć następującego:

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

Możesz użyć ContentDisposition lub ContentDispositionHeaderValue. Wywołanie ToString na wystąpieniu albo wykona kodowanie nazw plików za Ciebie.

sorenhk
źródło
6

Myślę, że może ci to pomóc.

Response.AddHeader("Content-Disposition", "attachment; filename=" + fileName)
Jarek
źródło
4
Nie obejmuje ucieczki. Co się stanie, jeśli nazwa pliku zawiera znak ;lub coś innego o specjalnym znaczeniu?
Sam
3

Musisz dodać nagłówek dyspozycji do odpowiedzi:

 response.StatusCode = HttpStatusCode.OK;
 response.Content = new StreamContent(result);
 response.AppendHeader("content-disposition", "attachment; filename=" + fileName);
 return response;
Carl Raymond
źródło
3
Nie obejmuje ucieczki. Co się stanie, jeśli nazwa pliku zawiera znak ;lub coś innego o specjalnym znaczeniu?
Sam
3

Jeśli używasz ASP.NET Core MVC, powyższe odpowiedzi są zawsze tak nieznacznie zmienione ...

W mojej metodzie akcji (która zwraca async Task<JsonResult>) dodaję wiersz (w dowolnym miejscu przed instrukcją return):

Response.Headers.Add("Content-Disposition", $"attachment; filename={myFileName}");
Piotr
źródło
Nie obejmuje ucieczki. Co jeśli nazwa pliku zawiera; czy coś innego o specjalnym znaczeniu?
KoalaBear
2

To powinno wystarczyć:

Response.AddHeader("Content-Disposition", "attachment;filename="+ YourFilename)
tucaz
źródło
2
Nie obejmuje ucieczki. Co się stanie, jeśli nazwa pliku zawiera znak ;lub coś innego o specjalnym znaczeniu?
Sam
0

Uwaga: ostatnia linia jest obowiązkowa.

Jeśli nie określimy Access-Control-Expose-Headers , nie otrzymamy nazwy pliku w interfejsie użytkownika.

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
źródło
0

Biorąc pod uwagę poprzednie odpowiedzi, należy uważać na zglobalizowane postacie.

Załóżmy, że nazwa pliku to: „ Esdrújula prenda ñame - güena.jpg

Nieprzetworzony wynik do pobrania: „EsdrĂşjula prenda Ă ± ame - gĂ¼ena.jpg” [Brzydki]

Wynik HtmlEncode do pobrania: „Esdr & _250; jula prenda & _241; ame - g & _252; ena.jpg” [Brzydki]

UrlEncode wynik do pobrania: „ Esdrújula + prenda + ñame + - + güena.jpg ” [OK]

Następnie prawie zawsze musisz użyć UrlEncode zamiast nazwy pliku . Co więcej, jeśli ustawisz nagłówek dyspozycji zawartości jako ciąg bezpośredni, musisz zapewnić otoczenie w cudzysłowie, aby uniknąć problemów ze zgodnością przeglądarki.

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

lub z pomocą klasową:

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

Plik System.Net.Mime. Klasa ContentDisposition obsługuje cudzysłowy.

Asereware
źródło