ASP.NET MVC में देखने / डाउनलोड करने के लिए एक फ़ाइल लौटा


304

मैं ASP.NET MVC में उपयोगकर्ता के लिए डेटाबेस में संग्रहीत फ़ाइलों को भेजने में एक समस्या का सामना कर रहा हूं। मैं जो चाहता हूं वह दो लिंक को सूचीबद्ध करने वाला एक दृश्य है, एक फाइल को देखने के लिए और ब्राउज़र को भेजे गए mimetype को यह निर्धारित करने के लिए कि इसे कैसे संभाला जाना चाहिए, और दूसरा डाउनलोड को मजबूर करने के लिए।

यदि मैं नामक फ़ाइल को देखने का विकल्प चुनता हूं SomeRandomFile.bakऔर ब्राउज़र के पास इस प्रकार की फ़ाइलों को खोलने के लिए संबद्ध प्रोग्राम नहीं है, तो मुझे डाउनलोड व्यवहार में डिफ़ॉल्ट रूप से कोई समस्या नहीं है। हालाँकि, अगर मैं किसी फ़ाइल को देखने के लिए कहता हूं SomeRandomFile.pdfया SomeRandomFile.jpgमैं चाहता हूं कि फ़ाइल बस खुले। लेकिन मैं एक डाउनलोड लिंक को साइड में रखना चाहता हूं ताकि मैं फ़ाइल प्रकार की परवाह किए बिना एक डाउनलोड प्रॉम्प्ट को मजबूर कर सकूं। इसका कोई मतलब भी है क्या?

मैंने कोशिश की है FileStreamResultऔर यह अधिकांश फाइलों के लिए काम करता है, इसका निर्माता डिफ़ॉल्ट रूप से फ़ाइल नाम को स्वीकार नहीं करता है, इसलिए अज्ञात फ़ाइलों को URL के आधार पर एक फ़ाइल नाम दिया जाता है (जो सामग्री प्रकार के आधार पर विस्तार देने के लिए नहीं जानता है)। यदि मैं फ़ाइल नाम निर्दिष्ट करके इसे लागू करता हूं, तो मैं ब्राउज़र को सीधे फ़ाइल खोलने की क्षमता खो देता हूं और मुझे डाउनलोड प्रॉम्प्ट मिलता है। क्या किसी और का इससे सामना हुआ है?

मैंने अब तक जो कोशिश की है, उसके ये उदाहरण हैं।

//Gives me a download prompt.
return File(document.Data, document.ContentType, document.Name);

//Opens if it is a known extension type, downloads otherwise (download has bogus name and missing extension)
return new FileStreamResult(new MemoryStream(document.Data), document.ContentType);

//Gives me a download prompt (lose the ability to open by default if known type)
return new FileStreamResult(new MemoryStream(document.Data), document.ContentType) {FileDownloadName = document.Name};

कोई सुझाव?


अद्यतन: यह प्रश्न बहुत से लोगों के साथ एक राग पर प्रहार करता है, इसलिए मुझे लगा कि मैं एक अपडेट पोस्ट करूंगा। अंतर्राष्ट्रीय वर्णों के बारे में Oskar द्वारा जोड़ा गया नीचे दिए गए स्वीकृत उत्तर पर चेतावनी पूरी तरह से मान्य है, और मैंने ContentDispositionकक्षा का उपयोग करने के कारण इसे कुछ बार मारा है । मैंने इसे ठीक करने के लिए अपने कार्यान्वयन को अपडेट कर दिया है। हालांकि नीचे दिया गया कोड ASP.NET Core (पूर्ण फ्रेमवर्क) ऐप में इस समस्या के मेरे सबसे हाल के अवतार से है, इसलिए इसे पुराने MVC एप्लिकेशन में न्यूनतम बदलाव के साथ काम करना चाहिए क्योंकि मैं System.Net.Http.Headers.ContentDispositionHeaderValueकक्षा का उपयोग कर रहा हूं ।

using System.Net.Http.Headers;

public IActionResult Download()
{
    Document document = ... //Obtain document from database context

    //"attachment" means always prompt the user to download
    //"inline" means let the browser try and handle it
    var cd = new ContentDispositionHeaderValue("attachment")
    {
        FileNameStar = document.FileName
    };
    Response.Headers.Add(HeaderNames.ContentDisposition, cd.ToString());

    return File(document.Data, document.ContentType);
}

// an entity class for the document in my database 
public class Document
{
    public string FileName { get; set; }
    public string ContentType { get; set; }
    public byte[] Data { get; set; }
    //Other properties left out for brevity
}

जवाबों:


430
public ActionResult Download()
{
    var document = ...
    var cd = new System.Net.Mime.ContentDisposition
    {
        // for example foo.bak
        FileName = document.FileName, 

        // always prompt the user for downloading, set to true if you want 
        // the browser to try to show the file inline
        Inline = false, 
    };
    Response.AppendHeader("Content-Disposition", cd.ToString());
    return File(document.Data, document.ContentType);
}

नोट: ऊपर दिया गया यह उदाहरण कोड फ़ाइल नाम में अंतर्राष्ट्रीय वर्णों के लिए ठीक से खाता नहीं है। संबंधित मानकीकरण के लिए RFC6266 देखें। मेरा मानना ​​है कि ASP.Net MVC की File()विधि के हाल के संस्करण और ContentDispositionHeaderValueवर्ग ठीक से इसके लिए जिम्मेदार हैं। - ओस्कर 2016-02-25


7
यदि मैं सही ढंग से याद करता हूं, तो इसे अन-कोट किया जा सकता है, जब तक कि फ़ाइल नाम का कोई स्थान न हो (मैंने इसे प्राप्त करने के लिए HttpUtility.UrlEncode () के माध्यम से मेरा भाग लिया)।
कीथ विलियम्स

22
नोट: यदि आप इसका उपयोग करते हैं और यह Inline = trueसुनिश्चित करते हैं कि 3-परम अधिभार का उपयोग न करें तो यह 3-परम के File()रूप में फ़ाइल-नाम लेता है। यह IE में काम करेगा , लेकिन क्रोम एक डुप्लिकेट हेडर की रिपोर्ट करेगा और छवि को प्रस्तुत करने से इनकार करेगा।
Faust

74
Var दस्तावेज़ किस प्रकार का है ...?
TTT

7
@ user1103990, यह आपका डोमेन मॉडल है।
डारिन दिमित्रोव

3
MVC 5 का उपयोग करना, अब सामग्री-वितरण हेडर की कोई आवश्यकता नहीं है, क्योंकि यह पहले से ही प्रतिक्रिया हेडर का हिस्सा है। लेकिन मुझे केवल FF में डाउनलोड डायलॉग, क्रोम में कोई डायलॉग और IE
लीजेंड्स

124

मुझे "दस्तावेज़" चर पर किसी भी प्रकार के संकेत के कारण स्वीकार किए गए उत्तर से परेशानी थी: var document = ...इसलिए मैं पोस्ट कर रहा हूं कि मेरे लिए किसी अन्य विकल्प के रूप में काम करने में परेशानी हो रही है।

public ActionResult DownloadFile()
{
    string filename = "File.pdf";
    string filepath = AppDomain.CurrentDomain.BaseDirectory + "/Path/To/File/" + filename;
    byte[] filedata = System.IO.File.ReadAllBytes(filepath);
    string contentType = MimeMapping.GetMimeMapping(filepath);

    var cd = new System.Net.Mime.ContentDisposition
    {
        FileName = filename,
        Inline = true,
    };

    Response.AppendHeader("Content-Disposition", cd.ToString());

    return File(filedata, contentType);
}

4
दस्तावेज़ चर सिर्फ एक वर्ग (POCO) था जो उस दस्तावेज़ के बारे में जानकारी का प्रतिनिधित्व करता था जिसे आप वापस करना चाहते थे। साथ ही स्वीकार किए गए जवाब पर पूछा गया था। यह एक ORM से, मैन्युअल रूप से निर्मित SQL क्वेरी से, फ़ाइल सिस्टम से (जैसा कि आपका जानकारी खींचता है), या कुछ अन्य डेटा स्टोर से आ सकता है। यह मूल प्रश्न के लिए अप्रासंगिक था जहां आपके दस्तावेज़ बाइट्स / फ़ाइलनाम / माइम प्रकार से आए थे, इसलिए इसे कोड को मैला नहीं करने के लिए छोड़ दिया गया था। हालांकि फ़ाइल सिस्टम का उपयोग करके एक उदाहरण के लिए धन्यवाद।
निक अल्ब्रेक्ट

1
यह समाधान सही तरीके से काम नहीं करता है जब फ़ाइल नाम में US-ASCII के बाहर अंतर्राष्ट्रीय वर्ण होते हैं।
Oskar Berggren

1
धन्यवाद, मेरा दिन बचा लिया :)
दीपेश

1
के लिए एक वैकल्पिक AppDomain.CurrentDomain.BaseDirectoryहै System.Web.HttpContext.Current.Server.MapPath("~")यह एक स्थानीय मशीन की तुलना में एक वास्तविक सर्वर पर बेहतर काम कर सकते हैं।
क्रिस थॉम्पसन

15

डारिन दिमित्रोव का जवाब सही है। बस एक अतिरिक्त:

Response.AppendHeader("Content-Disposition", cd.ToString());यदि आपकी प्रतिक्रिया में पहले से ही "सामग्री-विवाद" शीर्षक है तो ब्राउज़र फ़ाइल को रेंडर करने में विफल हो सकता है। उस स्थिति में, आप उपयोग करना चाह सकते हैं:

Response.Headers.Add("Content-Disposition", cd.ToString());

मुझे लगता है कि सामग्री प्रकार भी के लिए, प्रभाव है लगता है pdfफ़ाइल, अगर मैं सामग्री प्रकार सेट के रूप में System.Net.Mime.MediaTypeNames.Application.Octet, यह डाउनलोड भी जब मैं सेट के लिए मजबूर कर देंगे Inline = true, लेकिन अगर मैं के रूप में स्थापित Response.ContentType = MimeMapping.GetMimeMapping(filePath), यह है कि application/pdf, यह सही ढंग से नहीं बल्कि डाउनलोड से खोल सकते हैं
यू यांग जियान

Response.Headers.AddIIS एकीकृत पाइपलाइन मोड की आवश्यकता है। अतिरिक्त, भले ही ऐप पूल को एकीकृत के रूप में सेट किया गया हो, यह एक अपवाद फेंक देगा। समाधान। का उपयोग करें Response.AddHeader। SO थ्रेड देखें: stackoverflow.com/questions/22313167/…
रोलाण्ड

12

करने के लिए देखने फ़ाइल (उदाहरण के लिए txt):

return File("~/TextFileInRootDir.txt", MediaTypeNames.Text.Plain);

फ़ाइल डाउनलोड करने के लिए (उदाहरण के लिए txt):

return File("~/TextFileInRootDir.txt", MediaTypeNames.Text.Plain, "TextFile.txt");

नोट: फाइल डाउनलोड करने के लिए हमें fileDownloadName तर्क पास करना चाहिए


इस दृष्टिकोण के साथ एकमात्र समस्या यह है कि फ़ाइल नाम केवल तभी प्रदान किया जाता है जब आप उस विधि का उपयोग करते हैं जो डाउनलोड को मजबूर करता है। यह मुझे सही फ़ाइल नाम भेजने की अनुमति नहीं देता है जब मैं ब्राउज़र को यह निर्धारित करना चाहता हूं कि फ़ाइल कैसे खोलें। इसलिए बाहरी ऐप मेरे URL को फ़ाइल नाम के रूप में उपयोग करने का प्रयास करेगा। जो आमतौर पर डेटाबेस में दस्तावेज़ के लिए किसी प्रकार की आईडी होगी, आमतौर पर एक फ़ाइल एक्सटेंशन के साथ। यह एक बहुत गरीब के लिए बनाता है नाम फ़ाइल का उपयोग करने के लिए उपयोग किए गए URL से मेल नहीं खाता, परिदृश्य के लिए यह है कि यदि आप प्रदान नहीं करते हैं।
निक अल्ब्रेक्ट

Inlineसामग्री-विवाद पर संपत्ति का उपयोग करने से मुझे फ़ाइल नाम को डाउनलोड करने के लिए मजबूर करने या नहीं करने के व्यवहार से सेटिंग की क्षमता को अलग करने की अनुमति मिलती है।
निक अल्ब्रेक्ट

4

मेरा मानना ​​है कि यह उत्तर क्लीनर है, ( https://stackoverflow.com/a/3007668/550975 पर आधारित )

    public ActionResult GetAttachment(long id)
    {
        FileAttachment attachment;
        using (var db = new TheContext())
        {
            attachment = db.FileAttachments.FirstOrDefault(x => x.Id == id);
        }

        return File(attachment.FileData, "application/force-download", Path.GetFileName(attachment.FileName));
    }

9
अनुशंसित नहीं, सामग्री-विवाद स्पष्टता और अनुकूलता के लिए पसंदीदा तरीका है। stackoverflow.com/questions/10615797/…
निक अल्ब्रेक्ट 19

उसके लिए धन्यवाद। हालाँकि मैंने MIME प्रकार को बदल दिया है application/octet-streamऔर फिर भी फ़ाइल को दिखाए जाने के बजाय डाउनलोड किया जाना है, और यह संगत प्रतीत होता है।
सर्ज नगवां

1
सामग्री प्रकार के बारे में झूठ बोलना वास्तव में बुरा विचार लगता है। कुछ ब्राउज़र "सेव-ऑर-ओपन" संवाद में उपयोगकर्ता के लिए आवेदन का सुझाव देने के लिए सही सामग्री प्रकार पर निर्भर करते हैं।
Oskar Berggren

यदि आपका इरादा ब्राउज़र का सुझाव देना है तो यह ठीक है, लेकिन यह सवाल विशेष रूप से डाउनलोड को मजबूर करने के बारे में है ...
सर्ज सगन

@SerjSagan मुझे लगता है कि कुछ फ़ाइल प्रकारों को सीधे देखने या डिफ़ॉल्ट का उपयोग करने के लिए ब्राउजर के व्यवहार को दरकिनार करने के बारे में अधिक है, बजाय विकल्प को बचाने / खोलने के बीच की पेशकश के। अभी जेपीईजी के साथ प्रयास नहीं किया, इसलिए सटीक व्यवहार पर यकीन नहीं है।
Oskar Berggren

3

FileVirtualPath -> अनुसंधान \ ग्लोबल कार्यालय Review.pdf

public virtual ActionResult GetFile()
{
    return File(FileVirtualPath, "application/force-download", Path.GetFileName(FileVirtualPath));
}

5
यह पहले से ही पिछले उत्तर में उल्लिखित था और अनुशंसित नहीं है। अधिक विवरण के लिए निम्नलिखित प्रश्न देखें कि क्यों। stackoverflow.com/questions/10615797/…
निक अल्ब्रेक्ट

1
इस तरह यह सर्वर पर मेमोरी में फ़ाइल लोड करके सर्वर पर संसाधन का उपयोग नहीं करता, क्या मैं सही हूं?
इयान जोवेट

1
मुझे विश्वास है, क्योंकि आपके लिए कोई ज़रूरत नहीं है r @ @RashOverride
Hanna

1

एक एपीआई सेवा से एक पीडीएफ फाइल प्राप्त करने और इसे ब्राउज़र पर प्रतिक्रिया देने के लिए नीचे दिए गए कोड ने मेरे लिए काम किया - आशा है कि यह मदद करता है;

public async Task<FileResult> PrintPdfStatements(string fileName)
    {
         var fileContent = await GetFileStreamAsync(fileName);
         var fileContentBytes = ((MemoryStream)fileContent).ToArray();
         return File(fileContentBytes, System.Net.Mime.MediaTypeNames.Application.Pdf);
    }

2
जवाब के लिए धन्यवाद। आपने उस हिस्से को याद किया जहां मैं एक समाधान की तलाश कर रहा था जिसमें फ़ाइल का नाम निर्दिष्ट करने की क्षमता शामिल थी। आप बस बाइट्स वापस कर रहे हैं, इसलिए नाम URL से अनुमान लगाया जाएगा। मैं एक उदाहरण के रूप में पीडीएफ का उपयोग कर रहा था, लेकिन मुझे अपने मामले में कई अन्य फ़ाइल प्रकारों के लिए भी काम करने की आवश्यकता थी। मुझे यह उल्लेख करना चाहिए कि जब तक आप .NET फ्रेमवर्क 4.x और MVC <= 5. का उपयोग कर रहे हैं, तब तक आपका समाधान काम करेगा। यदि आप .NET कोर के खिलाफ चल रहे हैं, तो आपका सबसे अच्छा दांव उपयोग करना हैMicrosoft.AspNetCore.StaticFiles.FileExtensionContentTypeProvider
निक अल्ब्रेक्ट

@NickAlbrecht मैंने .Net Core का उपयोग नहीं किया था - उपरोक्त उदाहरण एक ब्राउज़र पर पीडीएफ की प्रतिक्रिया के लिए था। हालाँकि यदि आप कोशिश करना चाहते हैं: फ़ाइल (fileContentBytes, System.Net.Mime.MediaTypeNames.Application.Pdf, "आपका फ़ाइल नाम")। मुझे यकीन नहीं है कि अगर आपको अपना जवाब मिल गया है। कृपया मुझे बताएं कि क्या यह मदद करता है। हालांकि .Net कोर सुझाव के लिए धन्यवाद। अगर आपको मेरा उत्तर उपयोगी लगा हो तो कृपया वोट डालें।
जॉनी बॉय

मैंने पहले से ही इस मुद्दे को हल कर लिया है उत्तर के रूप में मैंने स्वीकार किया है। यदि आप उन्हें उपयोगी पाते हैं, तो मैं कुछ चेतावनी के संकेत दे रहा था। मेरा मूल प्रश्न 2011 था, इसलिए यह अब तक काफी दिनांकित है।
निक अल्ब्रेक्ट

@NickAlbrecht धन्यवाद। मुझे पता नहीं था कि आपने इसे हल किया है, मैंने देखा कि यह बहुत पुरानी पोस्ट है। मुझे कुछ async संबंधित उत्तरों की तलाश करते हुए यह मंच मिला, मैं async प्रक्रियाओं के लिए नया हूँ। मैं अपनी समस्या को हल करने में सक्षम था इसलिए सिर्फ साझा किया गया। आपके समय के लिए धन्यवाद।
जॉनी बॉय

0

एक्शन मेथड को FileResult को किसी स्ट्रीम, बाइट [], या फ़ाइल के वर्चुअल पथ के साथ वापस करना होगा। आपको डाउनलोड की जा रही फ़ाइल के सामग्री-प्रकार को भी जानना होगा। यहाँ एक नमूना (त्वरित / गंदा) उपयोगिता विधि है। नमूना वीडियो लिंक asp.net कोर का उपयोग करके फ़ाइलों को डाउनलोड करने के लिए कैसे

[Route("api/[controller]")]
public class DownloadController : Controller
{
    [HttpGet]
    public async Task<IActionResult> Download()
    {
        var path = @"C:\Vetrivel\winforms.png";
        var memory = new MemoryStream();
        using (var stream = new FileStream(path, FileMode.Open))
        {
            await stream.CopyToAsync(memory);
        }
        memory.Position = 0;
        var ext = Path.GetExtension(path).ToLowerInvariant();
        return File(memory, GetMimeTypes()[ext], Path.GetFileName(path));
    }

    private Dictionary<string, string> GetMimeTypes()
    {
        return new Dictionary<string, string>
        {
            {".txt", "text/plain"},
            {".pdf", "application/pdf"},
            {".doc", "application/vnd.ms-word"},
            {".docx", "application/vnd.ms-word"},
            {".png", "image/png"},
            {".jpg", "image/jpeg"},
            ...
        };
    }
}

आपके द्वारा संबद्ध किसी चीज़ से लिंक करना (उदाहरण के लिए एक लाइब्रेरी, टूल, उत्पाद, ट्यूटोरियल या वेबसाइट) यह खुलासा किए बिना कि आपका स्टैक ओवरफ़्लो पर स्पैम माना जाता है। देखें: क्या "अच्छा" आत्म प्रचार को दर्शाता है? , आत्म-प्रचार के बारे में कुछ सुझाव और सलाह , स्टैक ओवरफ्लो के लिए "स्पैम" की सटीक परिभाषा क्या है? , और क्या कुछ स्पैम बनाता है
शमूएल ल्यू

0

अगर, मेरी तरह, आप रेजर घटकों के माध्यम से इस विषय पर आए हैं जैसा कि आप ब्लेज़र सीख रहे हैं, तो आप पाएंगे कि आपको इस समस्या को हल करने के लिए बॉक्स के बाहर थोड़ा और सोचने की आवश्यकता है। यह एक माइनफ़ील्ड का एक सा है अगर (मेरी तरह भी) ब्लेज़र एमवीसी-प्रकार की दुनिया में आपका पहला फ़ॉरेस्ट है, क्योंकि इस तरह के 'मेनिअल' कार्यों के लिए प्रलेखन उतना उपयोगी नहीं है।

इसलिए, लेखन के समय, आप इसे वेनिला ब्लेज़र / रेज़र का उपयोग करके प्राप्त नहीं कर सकते हैं, बिना एमवीसी कंट्रोलर को फाइल डाउनलोड हिस्से को संभालने के लिए एम्बेड करना, जिसका एक उदाहरण नीचे दिया गया है:

using Microsoft.AspNetCore.Mvc;
using Microsoft.Net.Http.Headers;

[Route("api/[controller]")]
[ApiController]
public class FileHandlingController : ControllerBase
{
    [HttpGet]
    public FileContentResult Download(int attachmentId)
    {
        TaskAttachment taskFile = null;

        if (attachmentId > 0)
        {
            // taskFile = <your code to get the file>
            // which assumes it's an object with relevant properties as required below

            if (taskFile != null)
            {
                var cd = new System.Net.Http.Headers.ContentDispositionHeaderValue("attachment")
                {
                    FileNameStar = taskFile.Filename
                };

                Response.Headers.Add(HeaderNames.ContentDisposition, cd.ToString());
            }
        }

        return new FileContentResult(taskFile?.FileData, taskFile?.FileContentType);
    }
}

इसके बाद, सुनिश्चित करें कि आपका एप्लिकेशन स्टार्टअप (Startup.cs) एमवीसी का सही उपयोग करने के लिए कॉन्फ़िगर किया गया है और इसमें निम्न पंक्ति मौजूद है (इसे जोड़ें नहीं):

        services.AddMvc();

.. और फिर अंत में नियंत्रक से लिंक करने के लिए अपने घटक को संशोधित करें, उदाहरण के लिए (कस्टम वर्ग का उपयोग करके पुनरावृत्त आधारित उदाहरण):

    <tbody>
        @foreach (var attachment in yourAttachments)
        {
        <tr>
            <td><a href="api/FileHandling?attachmentId=@attachment.TaskAttachmentId" target="_blank">@attachment.Filename</a> </td>
            <td>@attachment.CreatedUser</td>
            <td>@attachment.Created?.ToString("dd MMM yyyy")</td>
            <td><ul><li class="oi oi-circle-x delete-attachment"></li></ul></td>
        </tr>
        }
        </tbody>

उम्मीद है कि यह किसी को भी, जो संघर्ष की तरह मदद करता है (मेरे जैसे!) ब्लेज़र के क्षेत्र में इस सरल सवाल का उचित जवाब पाने के लिए ...!


हालांकि यह आपके द्वारा की गई समस्या का सामना करने वाले अन्य लोगों के लिए उपयोगी हो सकता है, यह उनके लिए अधिक खोज योग्य होगा यदि आपने अपना प्रश्न एक शीर्षक के साथ पोस्ट किया है जो इंगित करता है कि यह ब्लेज़र के लिए अद्वितीय है, इसका उत्तर स्वयं दें, और यहां एक टिप्पणी जोड़ें जो किसी को पहुंचने का सुझाव दे। ब्लेज़र के बारे में समस्याओं के साथ यह सवाल आपके लिंक की जाँच करें। मुझे लगा कि मैं ASP.NET MVC के लिए इस मूल प्रश्न के साथ पहुँच रहा था, और ASP.NET कोर के लिए प्रासंगिक होने के लिए इसके उत्तर को स्वीकार कर रहा था। ब्लेज़र पूरी तरह से एक पूरी तरह से अलग जानवर है, जैसा कि आपने खोजा है।
निक अल्ब्रेक्ट
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.