एक ASP.NET MVC नियंत्रक एक छवि वापस कर सकते हैं?


455

क्या मैं एक नियंत्रक बना सकता हूं जो केवल छवि संपत्ति लौटाता है?

मैं इस तर्क को एक नियंत्रक के माध्यम से रूट करना चाहूंगा, जब भी कोई URL जैसे कि निम्नलिखित अनुरोध किया जाता है:

www.mywebsite.com/resource/image/topbanner

नियंत्रक दिखेगा topbanner.pngऔर उस छवि को सीधे क्लाइंट को भेज देगा ।

मैंने इसके उदाहरण देखे हैं जहां आपको एक दृश्य बनाना होगा - मैं एक दृश्य का उपयोग नहीं करना चाहता। मैं यह सब सिर्फ कंट्रोलर के साथ करना चाहता हूं।

क्या यह संभव है?


1
मैंने यहां इसी तरह का सवाल पूछा /programming/155906/creating-a-pStreet-photo-gallery-use-aspnet-mvc और इसे करने के लिए एक महान मार्गदर्शक ढूंढना समाप्त किया। मैंने इस गाइड के बाद एक ImageResult क्लास बनाया। https://blog.maartenballiauw.be/post/2008/05/13/aspnet-mvc-custom-actionresult.html
व्यारोक

2
यदि आप छवि को संशोधित करना चाहते हैं, तो सर्वश्रेष्ठ प्रदर्शन के लिए ImageResizing.Net HttpModule का उपयोग करें । यदि आप नहीं करते हैं, एक FilePathResult केवल कुछ प्रतिशत ओवरहेड जोड़ता है। URL पुनर्लेखन थोड़ा कम जोड़ता है।
लिलिथ नदी

1
MVC के बजाय WebApi नियंत्रक का उपयोग क्यों नहीं किया जा रहा है? ApiController class
A-Sharabiani

जवाबों:


534

आधार नियंत्रक फ़ाइल विधि का उपयोग करें।

public ActionResult Image(string id)
{
    var dir = Server.MapPath("/Images");
    var path = Path.Combine(dir, id + ".jpg"); //validate the path for security or use other means to generate the path.
    return base.File(path, "image/jpeg");
}

एक नोट के रूप में, यह काफी कुशल प्रतीत होता है। मैंने एक परीक्षण किया, जहां मैंने नियंत्रक ( http://localhost/MyController/Image/MyImage) और प्रत्यक्ष URL ( http://localhost/Images/MyImage.jpg) और परिणामों के माध्यम से छवि का अनुरोध किया :

  • एमवीसी: 7.6 मिलीसेकंड प्रति फोटो
  • प्रत्यक्ष: प्रति तस्वीर 6.7 मिलीसेकंड

नोट: यह एक अनुरोध का औसत समय है। स्थानीय मशीन पर हजारों अनुरोध करके औसत की गणना की गई थी, इसलिए योग में नेटवर्क विलंबता या बैंडविड्थ समस्या शामिल नहीं होनी चाहिए।


10
उन लोगों के लिए जो अब इस प्रश्न में आ रहे हैं, यह एक समाधान था जिसने मेरे लिए सबसे अच्छा काम किया।
क्लेरेंस क्लॉफस्टीन

177
यह सुरक्षित कोड नहीं है। उपयोगकर्ता को एक फ़ाइल नाम (पथ) इस तरह से पास करने का अर्थ है कि वे संभावित रूप से सर्वर पर कहीं से भी फ़ाइलों का उपयोग कर सकते हैं। लोगों को चेतावनी देना चाह सकते हैं कि वे इसका उपयोग न करें।
इयान मर्सर

7
जब तक आप मक्खी पर फ़ाइलों का निर्माण कर रहे हैं जब तक उन्हें ज़रूरत होती है और एक बार उन्हें बनाने के बाद उन्हें कैशिंग (कि हम क्या करते हैं)।
ब्रायन

15
@ घोड़ी- आप यह भी कर सकते हैं यदि आप एक प्रतिबंधित स्थान से फाइलें परोस रहे हैं जैसे कि आपके पास ऐसी छवियां हो सकती हैं App_Dataजो आपके एप्लिकेशन के कुछ उपयोगकर्ताओं द्वारा हस्ताक्षरित होनी चाहिए, लेकिन अन्य नहीं। उनकी सेवा करने के लिए एक नियंत्रक कार्रवाई का उपयोग करने से आप पहुंच को प्रतिबंधित कर सकते हैं।
रस कैम

8
जैसा कि अन्य ने उल्लेख किया है, अपने पथ निर्माण में सतर्क रहें, जैसा कि मैंने वास्तविक उत्पादन कोड देखा है जो उपयोगकर्ता को सावधानीपूर्वक निर्मित POST या क्वेरी स्ट्रिंग के साथ निर्देशिका को नेविगेट करने की अनुमति देता है: /../../../danger/someFileTheyTHoughtWasInaccessible
AaronLS

128

एमवीसी के रिलीज़ संस्करण का उपयोग करते हुए, यहाँ मैं क्या करूँ:

[AcceptVerbs(HttpVerbs.Get)]
[OutputCache(CacheProfile = "CustomerImages")]
public FileResult Show(int customerId, string imageName)
{
    var path = string.Concat(ConfigData.ImagesDirectory, customerId, "\\", imageName);
    return new FileStreamResult(new FileStream(path, FileMode.Open), "image/jpeg");
}

मैं स्पष्ट रूप से पथ निर्माण के संबंध में कुछ आवेदन विशिष्ट सामान यहाँ है, लेकिन FileStreamResult की वापसी अच्छी और सरल है।

मैंने आपकी रोजमर्रा की कॉल के लिए छवि (नियंत्रक को दरकिनार) के खिलाफ इस कार्रवाई के संबंध में कुछ प्रदर्शन परीक्षण किया और औसत के बीच का अंतर केवल 3 मिलीसेकंड था (नियंत्रक औसत 68ms था, गैर-नियंत्रक 65ms था)।

मैंने यहाँ उत्तर में उल्लिखित कुछ अन्य तरीकों की कोशिश की थी और प्रदर्शन हिट बहुत अधिक नाटकीय था ... कई समाधान प्रतिक्रियाएं 6x गैर-नियंत्रक (अन्य नियंत्रक एवीजी 340ms, गैर-नियंत्रक 65ms) जितनी थीं।


12
छवि के बारे में क्या संशोधित नहीं है? FileStreamResult को 304 भेजना चाहिए जब अंतिम अनुरोध के बाद से छवि को संशोधित नहीं किया जाता है।
दरियाल

आप Path.Combineसुरक्षित और अधिक पठनीय कोड के लिए कॉनैट के बजाय उपयोग कर सकते हैं ।
मार्सेल टूथ

101

डायलैंड की प्रतिक्रिया पर थोड़ा विस्तार करने के लिए:

तीन वर्ग FileResult वर्ग को लागू करते हैं :

System.Web.Mvc.FileResult
      System.Web.Mvc.FileContentResult
      System.Web.Mvc.FilePathResult
      System.Web.Mvc.FileStreamResult

वे सभी काफी आत्म व्याख्यात्मक हैं:

  • फ़ाइल पथ डाउनलोड के लिए जहां फ़ाइल डिस्क पर मौजूद है, का उपयोग करें FilePathResult- यह सबसे आसान तरीका है और आप स्ट्रीम का उपयोग करने से बचते हैं।
  • बाइट के लिए [] सरणियाँ (रिस्पांस के लिए एक जैसा FileContentResult। BinaryWrite), उपयोग करें ।
  • बाइट [] सरणियों के लिए जहां आप फ़ाइल डाउनलोड करना चाहते हैं (सामग्री-वितरण: अनुलग्नक), FileStreamResultनीचे के समान तरीके से उपयोग करें, लेकिन एक MemoryStreamऔर उपयोग के साथ GetBuffer()
  • Streamsउपयोग के लिए FileStreamResult। इसे FileStreamResult कहा जाता है, लेकिन Streamमुझे लगता है कि यह एक के साथ काम करता है MemoryStream

नीचे सामग्री-वितरण तकनीक का उपयोग करने का एक उदाहरण है (परीक्षण नहीं किया गया है):

    [AcceptVerbs(HttpVerbs.Post)]
    public ActionResult GetFile()
    {
        // No need to dispose the stream, MVC does it for you
        string path = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "App_Data", "myimage.png");
        FileStream stream = new FileStream(path, FileMode.Open);
        FileStreamResult result = new FileStreamResult(stream, "image/png");
        result.FileDownloadName = "image.png";
        return result;
    }

2
इस पोस्ट का कंटेंट-डिस्पोजल पार्ट बेहद मददगार था
डिएगो

VS मुझे बता रहा है कि FileStream () अप्रचलित है।
MrBoJangles

1
कुछ ध्यान दें: यदि आपके पास आपके फ़ाइल नाम में एक अल्पविराम है, तो क्रोम इसे "बहुत अधिक हेडर प्राप्त" त्रुटि के साथ अस्वीकार कर देगा। इसलिए सभी कॉमा को "-" या "" से बदलें।
क्रिस एस

केवल वेब एपीआई नियंत्रकों का उपयोग करके कोई ऐसा कैसे करेगा?
Zapnologica

74

यदि आप इसे वापस करने से पहले छवि को संशोधित करना चाहते हैं तो यह उपयोगी हो सकता है:

public ActionResult GetModifiedImage()
{
    Image image = Image.FromFile(Path.Combine(Server.MapPath("/Content/images"), "image.png"));

    using (Graphics g = Graphics.FromImage(image))
    {
        // do something with the Graphics (eg. write "Hello World!")
        string text = "Hello World!";

        // Create font and brush.
        Font drawFont = new Font("Arial", 10);
        SolidBrush drawBrush = new SolidBrush(Color.Black);

        // Create point for upper-left corner of drawing.
        PointF stringPoint = new PointF(0, 0);

        g.DrawString(text, drawFont, drawBrush, stringPoint);
    }

    MemoryStream ms = new MemoryStream();

    image.Save(ms, System.Drawing.Imaging.ImageFormat.Png);

    return File(ms.ToArray(), "image/png");
}

1
धन्यवाद। यह उस परिदृश्य के लिए एकदम सही है, जहां क्लाइंट को प्रमाणीकरण की आवश्यकता वाली छवि को डाउनलोड करने के लिए प्रॉक्सी की आवश्यकता होती है जो क्लाइंट साइड पर नहीं किया जा सकता है।
हांग

1
आप एक मूल 3 मूल वस्तुओं के निपटान के लिए भूल रहे हैं: फ़ॉन्ट, सॉलिडब्रश और छवि।
वॉट

3
यहां सुधार का सुझाव दिया गया है: आप एक मेमोरी स्ट्रीम बनाते हैं, डेटा लिखते हैं और फिर .ToArray () का उपयोग करके डेटा के साथ एक फ़ाइल परिणाम बनाते हैं। आप बस ms.Seek (0, SeekOrigin.Begin) को कॉल कर सकते हैं और फिर फ़ाइल (ms) वापस कर सकते हैं। image / png ") // स्ट्रीम को स्वयं वापस करें
Quango

12

आप अपना खुद का एक्सटेंशन बना सकते हैं और इस तरह से कर सकते हैं।

public static class ImageResultHelper
{
    public static string Image<T>(this HtmlHelper helper, Expression<Action<T>> action, int width, int height)
            where T : Controller
    {
        return ImageResultHelper.Image<T>(helper, action, width, height, "");
    }

    public static string Image<T>(this HtmlHelper helper, Expression<Action<T>> action, int width, int height, string alt)
            where T : Controller
    {
        var expression = action.Body as MethodCallExpression;
        string actionMethodName = string.Empty;
        if (expression != null)
        {
            actionMethodName = expression.Method.Name;
        }
        string url = new UrlHelper(helper.ViewContext.RequestContext, helper.RouteCollection).Action(actionMethodName, typeof(T).Name.Remove(typeof(T).Name.IndexOf("Controller"))).ToString();         
        //string url = LinkBuilder.BuildUrlFromExpression<T>(helper.ViewContext.RequestContext, helper.RouteCollection, action);
        return string.Format("<img src=\"{0}\" width=\"{1}\" height=\"{2}\" alt=\"{3}\" />", url, width, height, alt);
    }
}

public class ImageResult : ActionResult
{
    public ImageResult() { }

    public Image Image { get; set; }
    public ImageFormat ImageFormat { get; set; }

    public override void ExecuteResult(ControllerContext context)
    {
        // verify properties 
        if (Image == null)
        {
            throw new ArgumentNullException("Image");
        }
        if (ImageFormat == null)
        {
            throw new ArgumentNullException("ImageFormat");
        }

        // output 
        context.HttpContext.Response.Clear();
        context.HttpContext.Response.ContentType = GetMimeType(ImageFormat);
        Image.Save(context.HttpContext.Response.OutputStream, ImageFormat);
    }

    private static string GetMimeType(ImageFormat imageFormat)
    {
        ImageCodecInfo[] codecs = ImageCodecInfo.GetImageEncoders();
        return codecs.First(codec => codec.FormatID == imageFormat.Guid).MimeType;
    }
}
public ActionResult Index()
    {
  return new ImageResult { Image = image, ImageFormat = ImageFormat.Jpeg };
    }
    <%=Html.Image<CapchaController>(c => c.Index(), 120, 30, "Current time")%>

10

आप सीधे प्रतिक्रिया के लिए लिख सकते हैं, लेकिन तब यह परीक्षण करने योग्य नहीं है। यह एक ActionResult को वापस करने के लिए पसंद किया जाता है जिसने निष्पादन को स्थगित कर दिया है। यहाँ मेरा resusable StreamResult है:

public class StreamResult : ViewResult
{
    public Stream Stream { get; set; }
    public string ContentType { get; set; }
    public string ETag { get; set; }

    public override void ExecuteResult(ControllerContext context)
    {
        context.HttpContext.Response.ContentType = ContentType;
        if (ETag != null) context.HttpContext.Response.AddHeader("ETag", ETag);
        const int size = 4096;
        byte[] bytes = new byte[size];
        int numBytes;
        while ((numBytes = Stream.Read(bytes, 0, size)) > 0)
            context.HttpContext.Response.OutputStream.Write(bytes, 0, numBytes);
    }
}

9

सरल क्यों न जाएं और टिल्ड ~ऑपरेटर का उपयोग करें ?

public FileResult TopBanner() {
  return File("~/Content/images/topbanner.png", "image/png");
}

6

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

आप निश्चित रूप से कर सकते हैं। इन चरणों को आज़माएं:

  1. छवि को डिस्क से बाइट सरणी में लोड करें
  2. उस मामले में छवि को कैश करें जब आप छवि के लिए अधिक अनुरोधों की अपेक्षा करते हैं और डिस्क I / O नहीं चाहते हैं (मेरा नमूना इसे नीचे कैश नहीं करता है)
  3. प्रतिक्रिया के माध्यम से माइम प्रकार बदलें
  4. Response.BinaryWrite छवि बाइट सरणी से बाहर

यहाँ कुछ नमूना कोड दिया गया है:

string pathToFile = @"C:\Documents and Settings\some_path.jpg";
byte[] imageData = File.ReadAllBytes(pathToFile);
Response.ContentType = "image/jpg";
Response.BinaryWrite(imageData);

उम्मीद है की वो मदद करदे!


4
और नियंत्रक की कार्रवाई में यह कैसे दिखेगा?
सीआरएस

5

समाधान 1: छवि URL से किसी दृश्य में एक छवि प्रदान करने के लिए

आप अपनी खुद की एक्सटेंशन विधि बना सकते हैं:

public static MvcHtmlString Image(this HtmlHelper helper,string imageUrl)
{
   string tag = "<img src='{0}'/>";
   tag = string.Format(tag,imageUrl);
   return MvcHtmlString.Create(tag);
}

फिर इसका उपयोग करें जैसे:

@Html.Image(@Model.ImagePath);

समाधान 2: डेटाबेस से छवि को प्रस्तुत करने के लिए

एक नियंत्रक विधि बनाएं जो नीचे की तरह छवि डेटा लौटाता है

public sealed class ImageController : Controller
{
  public ActionResult View(string id)
  {
    var image = _images.LoadImage(id); //Pull image from the database.
    if (image == null) 
      return HttpNotFound();
    return File(image.Data, image.Mime);
  }
}

और इसे इस तरह देखें:

@ { Html.RenderAction("View","Image",new {id=@Model.ImageId})}

किसी भी HTML में इस कार्यक्षेत्र से प्रदान की गई छवि का उपयोग करने के लिए, उपयोग करें

<img src="http://something.com/image/view?id={imageid}>

5

इसने मेरे लिए काम किया। जब से मैं एक SQL सर्वर डेटाबेस पर छवियों को संग्रहीत कर रहा हूँ।

    [HttpGet("/image/{uuid}")]
    public IActionResult GetImageFile(string uuid) {
        ActionResult actionResult = new NotFoundResult();
        var fileImage = _db.ImageFiles.Find(uuid);
        if (fileImage != null) {
            actionResult = new FileContentResult(fileImage.Data,
                fileImage.ContentType);
        }
        return actionResult;
    }

ऊपर स्निपेट _db.ImageFiles.Find(uuid)में db (EF संदर्भ) में छवि फ़ाइल रिकॉर्ड की खोज कर रहा है। यह एक FileImage ऑब्जेक्ट देता है जो मॉडल के लिए बनाई गई एक कस्टम क्लास है और फिर इसे FileContentResult के रूप में उपयोग करता है।

public class FileImage {
   public string Uuid { get; set; }
   public byte[] Data { get; set; }
   public string ContentType { get; set; }
}

4

आप किसी फ़ाइल जैसे दृश्य, सामग्री आदि को वापस करने के लिए फ़ाइल का उपयोग कर सकते हैं

 public ActionResult PrintDocInfo(string Attachment)
            {
                string test = Attachment;
                if (test != string.Empty || test != "" || test != null)
                {
                    string filename = Attachment.Split('\\').Last();
                    string filepath = Attachment;
                    byte[] filedata = System.IO.File.ReadAllBytes(Attachment);
                    string contentType = MimeMapping.GetMimeMapping(Attachment);

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

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

                    return File(filedata, contentType);          
                }
                else { return Content("<h3> Patient Clinical Document Not Uploaded</h3>"); }

            }

3

ContentResult को देखें। यह एक स्ट्रिंग लौटाता है, लेकिन इसका उपयोग आपके खुद के द्विआधारीResult जैसी कक्षा बनाने के लिए किया जा सकता है।


2
if (!System.IO.File.Exists(filePath))
    return SomeHelper.EmptyImageResult(); // preventing JSON GET/POST exception
else
    return new FilePathResult(filePath, contentType);

SomeHelper.EmptyImageResult()FileResultमौजूदा छवि (उदाहरण के लिए पारदर्शी 1x1) के साथ वापस आना चाहिए ।

यह सबसे आसान तरीका है यदि आपके पास स्थानीय ड्राइव पर संग्रहीत फ़ाइलें हैं। यदि फ़ाइलें हैं byte[]या stream- तो उपयोग करें FileContentResultया FileStreamResultजैसा कि डायलन ने सुझाव दिया है।


1

मुझे दो विकल्प दिखाई देते हैं:

1) अपने स्वयं के IViewEngine को लागू करें और अपने इच्छित "छवि" विधि में अपने ImageViewEngine के लिए उपयोग कर रहे हैं नियंत्रक की ViewEngine संपत्ति सेट करें।

2) एक दृश्य का उपयोग करें :-)। बस कंटेंट टाइप आदि को बदल दें।


1
दृश्य में अतिरिक्त स्थान या CRLF के कारण यह समस्या हो सकती है।
एलन हसन

2
मैं अपनी पिछली पोस्ट में गलत था ... msdn.microsoft.com/en-us/library/… आप WebImage क्लास और WebImage.Write को एक दृश्य में उपयोग कर सकते हैं :)
Elan Hasson

1

आप HttpContext.Response का उपयोग कर सकते हैं और सीधे इसे लिख सकते हैं (WriteFile () आप के लिए काम कर सकते हैं) और फिर ActionResult के बजाय अपनी कार्रवाई से ContentResult लौटा सकते हैं।

अस्वीकरण: मैंने यह कोशिश नहीं की है, यह उपलब्ध एपीआई को देखने पर आधारित है। :-)


1
हाँ, मैंने अभी देखा है ContentResult केवल तार का समर्थन करता है, लेकिन यह आसान है कि आप अपना खुद का ActionResult आधारित वर्ग बना सकें।
लीपी

1

नीचे कोड System.Drawing.Bitmapछवि लोड करने के लिए उपयोग करता है।

using System.Drawing;
using System.Drawing.Imaging;

public IActionResult Get()
{
    string filename = "Image/test.jpg";
    var bitmap = new Bitmap(filename);

    var ms = new System.IO.MemoryStream();
    result.Save(ms, ImageFormat.Jpeg);
    ms.Position = 0;
    return new FileStreamResult(ms, "image/jpeg");
}

0

मुझे भी ऐसी ही आवश्यकता का सामना करना पड़ा,

तो मेरे मामले में मैं छवि फ़ोल्डर पथ के साथ नियंत्रक के लिए एक अनुरोध करता हूं, जो बदले में एक ImageResult ऑब्जेक्ट को वापस भेजता है।

निम्नलिखित कोड स्निपेट कार्य का वर्णन करता है:

var src = string.Format("/GenericGrid.mvc/DocumentPreviewImageLink?fullpath={0}&routingId={1}&siteCode={2}", fullFilePath, metaInfo.RoutingId, da.SiteCode);

                if (enlarged)
                    result = "<a class='thumbnail' href='#thumb'>" +
                        "<img src='" + src + "' height='66px' border='0' />" +
                        "<span><img src='" + src + "' /></span>" +
                        "</a>";
                else
                    result = "<span><img src='" + src + "' height='150px' border='0' /></span>";

और छवि पथ से नियंत्रक में मैं छवि का उत्पादन करता हूं और इसे कॉलर को वापस करता हूं

try
{
  var file = new FileInfo(fullpath);
  if (!file.Exists)
     return string.Empty;


  var image = new WebImage(fullpath);
  return new ImageResult(new MemoryStream(image.GetBytes()), "image/jpg");


}
catch(Exception ex)
{
  return "File Error : "+ex.ToString();
}

0

छवि पढ़ें, इसे रूपांतरित करें byte[], फिर File()सामग्री प्रकार के साथ वापस लौटें ।

public ActionResult ImageResult(Image image, ImageFormat format, string contentType) {
  using (var stream = new MemoryStream())
    {
      image.Save(stream, format);
      return File(stream.ToArray(), contentType);
    }
  }
}

यहाँ usings हैं:

using System.Drawing;
using System.Drawing.Imaging;
using System.IO;
using Microsoft.AspNetCore.Mvc;
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.