How to retrieve all key-value pairs in a resource file located in App_GlobalResources - asp.net

In my ASP.NET MVC application, I manage localized texts in .resx files located in App_GlobalResources folder. I am able to retrieve any text value in any file knowing its key.
Now, I want to retrieve all key/value pairs in a particular resource file in order to write the result to some JavaScript. A search revealed that I might be able to use ResXResourceReader class and iterate through the pairs; however the class is unfortunately located in the System.Windows.Forms.dll and I don't want to wire that dependency to my web app. Is there any other way I can implement this feature?

I've found the solution. Now no need to reference Forms.dll.
public class ScriptController : BaseController
{
private static readonly ResourceSet ResourceSet =
Resources.Controllers.Script.ResourceManager.GetResourceSet(CurrentCulture, true, true);
public ActionResult GetResources()
{
var builder = new StringBuilder();
builder.Append("var LocalizedStrings = {");
foreach (DictionaryEntry entry in ResourceSet)
{
builder.AppendFormat("{0}: \"{1}\",", entry.Key, entry.Value);
}
builder.Append("};");
Response.ContentType = "application/x-javascript";
Response.ContentEncoding = Encoding.UTF8;
return Content(builder.ToString());
}
}

Okay, no other answer. Seems like referencing Forms.dll is the only way right now. Here's the code I came up with.
public class ScriptController : BaseController
{
private const string ResxPathTemplate = "~/App_GlobalResources/script{0}.resx";
public ActionResult GetResources()
{
var resxPath = Server.MapPath(string.Format(ResxPathTemplate, string.Empty));
var resxPathLocalized = Server.MapPath(string.Format(ResxPathTemplate,
"." + CurrentCulture));
var pathToUse = System.IO.File.Exists(resxPathLocalized)
? resxPathLocalized
: resxPath;
var builder = new StringBuilder();
using (var rsxr = new ResXResourceReader(pathToUse))
{
builder.Append("var resources = {");
foreach (DictionaryEntry entry in rsxr)
{
builder.AppendFormat("{0}: \"{1}\",", entry.Key, entry.Value);
}
builder.Append("};");
}
Response.ContentType = "application/x-javascript";
Response.ContentEncoding = Encoding.UTF8;
return Content(builder.ToString());
}
}

Related

How to use parameters in Twitter Bootstrap loaded dynamically from the database

I have a website where I need multiple themes.
So www.mysite.com/Client1/ uses red buttons and www.mysite.com/Client2/ uses blue buttons.
The number of clients are dynamic stores in a DB, and the colors are also stored in the DB. Can be changed at anytime by the client.
Currently I am using Twitter Bootstrap LESS files and ASP MVC Optimization (bundle).
My App_Start BundleConfig looks like this:
var cssTransformer = new CssTransformer();
var stylesBundle = new StyleBundle("~/Content/bootstrap");
.Include("~/Content/less/bootstrap.less")
stylesBundle.Transforms.Add(cssTransformer);
bundles.Add(stylesBundle);
In variables.less
#btnPrimaryBackground: #linkColor;
The color of #btnPrimaryBackground should change when different urls are called.
How do I change the less variable to use a parameter from my another source (database or other)?
Since Web Optimization does not play nice with dynamic dontent, I decided to not use it.
Instead I have made am ASP MVC ActionResult for LESS, and reference that.
<link rel="stylesheet" type="text/css" href="#Url.Action("Styles", "Theme")">
My ASP MVC Controller looks like this:
public class ThemeController : Controller
{
public ActionResult Styles()
{
var parameters = new Dictionary<string, string>
{
{"themeColor1", "Get theme color 1 here"},
{"themeColor2", "Get theme color 2 here"}
};
var themeLessFilePath = Server.MapPath("~/Content/less/theme.less");
using (var stream = System.IO.File.OpenRead(themeLessFilePath))
{
return new DotLessResult(stream, parameters, true);
}
}
}
And the LESS ActionResult like this:
public class DotLessResult : ActionResult
{
public IDictionary<string, string> Parameters { get; set; }
public string Less { get; set; }
public bool Minify { get; set; }
public DotLessResult(string less, IDictionary<string, string> parameters = null, bool minify = false)
{
Less = less;
Parameters = parameters ?? new Dictionary<string, string>();
Minify = minify;
}
public DotLessResult(Stream stream, IDictionary<string, string> parameters = null, bool minify = false)
: this(new StreamReader(stream).ReadToEnd(), parameters, minify) { }
public override void ExecuteResult(ControllerContext context)
{
var output = Less;
//TODO: Not the way to do this!
foreach (var key in Parameters.Keys)
{
output = Regex.Replace(output, #"#" + key + #":\s*\S+;", "#" + key + ":" + Parameters[key] + ";");
}
var lessEngine = dotless.Core.LessWeb.GetEngine(new DotlessConfiguration { MinifyOutput = Minify, MapPathsToWeb = true, Web = true, CacheEnabled = false});
var css = lessEngine.TransformToCss(output, (string)null);
context.HttpContext.Response.ContentType = "text/css";
using (var writer = new StreamWriter(context.HttpContext.Response.OutputStream, Encoding.UTF8))
{
writer.Write(css);
writer.Flush();
}
}
}
Its NOT the best solution, but it works on my machine TM.
Dont forget to implement some kind of output caching as it will most like be hit alot, and not change very often.

Create the own VirtualPathProvider in MVC4?

I'm suffering trying to get some views from a library to the main project. I was starting to read about creating your own VirtualPathProvider implementation here: Using VirtualPathProvider to load ASP.NET MVC views from DLLs
I had to set my view = EmbbebedResource to get the resource from the library. But now is throwing another error.
In the header of my partial view I had the following:
#model Contoso.ExercisesLibrary.AbsoluteArithmetic.Problem1
And the error says: External component has thrown an exception. c:\Users\Oscar\AppData\Local\Temp\Temporary ASP.NET Files\root\4f78c765\7f9a47c6\App_Web_contoso.exerciseslibrary.absolutearithmetic.view1.cshtml.38e14c22.y-yjyt6g.0.cs(46): error CS0103: The name 'model' does not exist in the current context
I don't know why the compiler tells that cannot recognized my model. When I'm in design mode, I can see the compiler that the check is all right.
Check the image
What am I doing wrong o what am I missing?
Thanks in advance.
Try adding an #inherits directive to the top of your razor view:
#inherits System.Web.Mvc.WebViewPage
#model Contoso.ExercisesLibrary.AbsoluteArithmetic.Problem1
The reason you need this is because your view comes from an embedded resource and not from the standard ~/Views location. And as you know inside ~/Views there's a file called web.config. And inside this file there's a pageBaseType="System.Web.Mvc.WebViewPage" directive indicating that all Razor files inside ~/Views should inherit from this base type. But since your view is now coming from an unknown location you have nowhere specified that it should be a System.Web.Mvc.WebViewPage. And all the MVC specific stuff such as models, HTML helpers, ... are defined in this base class.+
I faced this issue "The name 'model' does not exist in the current context". What I did was added same "areas" folder structure (from my embedded mvc project) to my main MVC project (Areas/AnualReports/Views/) and copied web.config (default web.config from views folder, not the one from root) to Views folder which solved the issue. I am not sure this will work in your case.
Update:
Adding web.config (from views folder) to root "areas" folder in main MVC project also works.
I have the same problem as you so after all searches I got working solution
Create your own WebViewPage based abstract class (generic for model and non generic)
public abstract class MyOwnViewPage<TModel> : WebViewPage<TModel> { }
public abstract class MyOwnViewPage : WebViewPage { }
Next create VirtualFile based class or embedded view's
class AssemblyResourceFile : VirtualFile
{
private readonly IDictionary<string, Assembly> _nameAssemblyCache;
private readonly string _assemblyPath;
private readonly string _webViewPageClassName;
public string LayoutPath { get; set; }
public string ViewStartPath { get; set; }
public AssemblyResourceFile(IDictionary<string, Assembly> nameAssemblyCache, string virtualPath) :
base(virtualPath)
{
_nameAssemblyCache = nameAssemblyCache;
_assemblyPath = VirtualPathUtility.ToAppRelative(virtualPath);
LayoutPath = "~/Views/Shared/_Layout.cshtml";
ViewStartPath = "~/Views/_ViewStart.cshtml";
_webViewPageClassName = typeofMyOwnViewPage).ToString();
}
// Please change Open method for your scenario
public override Stream Open()
{
string[] parts = _assemblyPath.Split(new[] { '/' }, 4);
string assemblyName = parts[2];
string resourceName = parts[3].Replace('/', '.');
Assembly assembly;
lock (_nameAssemblyCache)
{
if (!_nameAssemblyCache.TryGetValue(assemblyName, out assembly))
{
var assemblyPath = Path.Combine(HttpRuntime.BinDirectory, assemblyName);
assembly = Assembly.LoadFrom(assemblyPath);
_nameAssemblyCache[assemblyName] = assembly;
}
}
Stream resourceStream = null;
if (assembly != null)
{
resourceStream = assembly.GetManifestResourceStream(resourceName);
if (resourceName.EndsWith(".cshtml"))
{
//the trick is here. We must correct our embedded view
resourceStream = CorrectView(resourceName, resourceStream);
}
}
return resourceStream;
}
public Stream CorrectView(string virtualPath, Stream stream)
{
var reader = new StreamReader(stream, Encoding.UTF8);
var view = reader.ReadToEnd();
stream.Close();
var ourStream = new MemoryStream();
var writer = new StreamWriter(ourStream, Encoding.UTF8);
var modelString = "";
var modelPos = view.IndexOf("#model");
if (modelPos != -1)
{
writer.Write(view.Substring(0, modelPos));
var modelEndPos = view.IndexOfAny(new[] { '\r', '\n' }, modelPos);
modelString = view.Substring(modelPos, modelEndPos - modelPos);
view = view.Remove(0, modelEndPos);
}
writer.WriteLine("#using System.Web.Mvc");
writer.WriteLine("#using System.Web.Mvc.Ajax");
writer.WriteLine("#using System.Web.Mvc.Html");
writer.WriteLine("#using System.Web.Routing");
var basePrefix = "#inherits " + _webViewPageClassName;
if (virtualPath.ToLower().Contains("_viewstart"))
{
writer.WriteLine("#inherits System.Web.WebPages.StartPage");
}
else if (modelString == "#model object")
{
writer.WriteLine(basePrefix + "<dynamic>");
}
else if (!string.IsNullOrEmpty(modelString))
{
writer.WriteLine(basePrefix + "<" + modelString.Substring(7) + ">");
}
else
{
writer.WriteLine(basePrefix);
}
writer.Write(view);
writer.Flush();
ourStream.Position = 0;
return ourStream;
}
}
Next create VirtualPathProvider based class (modify it for your purposes)
public class AssemblyResPathProvider : VirtualPathProvider
{
private readonly Dictionary<string, Assembly> _nameAssemblyCache;
private string _layoutPath;
private string _viewstartPath;
public AssemblyResPathProvider(string layout, string viewstart)
{
_layoutPath = layout;
_viewstartPath = viewstart;
_nameAssemblyCache = new Dictionary<string, Assembly>(StringComparer.InvariantCultureIgnoreCase);
}
private bool IsAppResourcePath(string virtualPath)
{
string checkPath = VirtualPathUtility.ToAppRelative(virtualPath);
bool bres1 = checkPath.StartsWith("~/App_Resource/",
StringComparison.InvariantCultureIgnoreCase);
bool bres2 = checkPath.StartsWith("/App_Resource/",
StringComparison.InvariantCultureIgnoreCase);
//todo: fix this
if (checkPath.EndsWith("_ViewStart.cshtml"))
{
return false;
}
if (checkPath.EndsWith("_ViewStart.vbhtml"))
{
return false;
}
return ((bres1 || bres2));
}
public override bool FileExists(string virtualPath)
{
return (IsAppResourcePath(virtualPath) ||
base.FileExists(virtualPath));
}
public override VirtualFile GetFile(string virtualPath)
{
if (IsAppResourcePath(virtualPath))
{
// creating AssemblyResourceFile instance
return new AssemblyResourceFile(_nameAssemblyCache, virtualPath,_layoutPath,virtualPath);
}
return base.GetFile(virtualPath);
}
public override CacheDependency GetCacheDependency(
string virtualPath,
IEnumerable virtualPathDependencies,
DateTime utcStart)
{
if (IsAppResourcePath(virtualPath))
{
return null;
}
return base.GetCacheDependency(virtualPath,
virtualPathDependencies, utcStart);
}
}
At last register your AssemblyResPathProvider in global.asax
string _layoutPath = "~/Views/Shared/_Layout.cshtml";
string _viewstarPath = "~/Views/_ViewStart.cshtml";
HostingEnvironment.RegisterVirtualPathProvider(new AssemblyResPathProvider(_layoutPath,_viewstarPath));
This is not ideal solution but its working for me good. Cheers!
In my case, the solution was to make the virtual Path start with "~Views/" - just like any normal view.
Not working: ~/VIRTUAL/Home/Index.cshtml
Working: ~/Views/VIRTUAL/Home/Index.cshtml
I think, this has to do with the web.config lying around in ~/Views and defining a lot of stuff for the views. Maybe anybody can give more information.
Hope that helps anyway.

Using Url.Content() when sending an html email with images

I need my app to send a confirmation email to a user. I have used the following method to render the view as a string:
public string RenderViewToString<T>(string viewPath, T model)
{
using (var writer = new StringWriter())
{
var view = new WebFormView(viewPath);
var vdd = new ViewDataDictionary<T>(model);
var viewCxt = new ViewContext(ControllerContext, view, vdd, new TempDataDictionary(), writer);
viewCxt.View.Render(viewCxt, writer);
return writer.ToString();
}
}
which I got from here. It works great, however my images aren't being included. I'm using:
<img src="<%:Url.Content("~/Resource/confirmation-email/imageName.png") %>"
which is giving me
http://resource/confirmation-email/imageName.png
This works fine when viewing the page on the site, however the image links don't work in the email.
I need it to give me me:
http://domain.com/application/resource/confirmation-email/imageName.png
I've also tried using:
VirtualPathUtility.ToAbsolute()
This is what I used on a site recently:
public static string ResolveServerUrl(string serverUrl, bool forceHttps = false, bool getVirtualPath = true)
{
if (getVirtualPath)
serverUrl = VirtualPathUtility.ToAbsolute(serverUrl);
if (serverUrl.IndexOf("://") > -1)
return serverUrl;
string newUrl = serverUrl;
Uri originalUri = System.Web.HttpContext.Current.Request.Url;
newUrl = (forceHttps ? "https" : originalUri.Scheme) + "://" + originalUri.Authority + newUrl;
return newUrl;
}
I could then use it to generate Absolute urls by doing Core.ResolveServerUrl("~/Resource/confirmation-email/imageName.png"); (assuming you wrap the static function in a class named Core)
HTH
There isn't a way to do this. You can add the following extension method.
using System.Web.Mvc;
public static class UrlHelperExtensions
{
public static string ToAbsoluteUrl(this UrlHelper helper, string relativeUrl) {
if (Request.IsSecureConnection)
return string.Format("https://{0}{1}", Request.Url.Host, Page.ResolveUrl(relativeUrl));
else
return string.Format("http://{0}{1}", Request.Url.Host, Page.ResolveUrl(relativeUrl));
}
}
Which you can then call like so
<img src="<%:Url.ToAbsoluteUrl("~/Resource/confirmation-email/imageName.png") %>" ...

How to unit test code that uses HostingEnvironment.MapPath

I have some code that uses HostingEnvironment.MapPath which I would like to unit test.
How can I setup HostingEnvironment so that it returns a path and not null in my unit test (mstest) project?
Why would you have a code that depends on HostingEnvironment.MapPath in an ASP.NET MVC application where you have access to objects like HttpServerUtilityBase which allow you to achieve this and which can be easily mocked and unit tested?
Let's take an example: a controller action which uses the abstract Server class that we want to unit test:
public class HomeController : Controller
{
public ActionResult Index()
{
var file = Server.MapPath("~/App_Data/foo.txt");
return View((object)file);
}
}
Now, there are many ways to unit test this controller action. Personally I like using the MVcContrib.TestHelper.
But let's see how we can do this using a mocking framework out-of-the-box. I use Rhino Mocks for this example:
[TestMethod]
public void Index_Action_Should_Calculate_And_Pass_The_Physical_Path_Of_Foo_As_View_Model()
{
// arrange
var sut = new HomeController();
var server = MockRepository.GeneratePartialMock<HttpServerUtilityBase>();
var context = MockRepository.GeneratePartialMock<HttpContextBase>();
context.Expect(x => x.Server).Return(server);
var expected = #"c:\work\App_Data\foo.txt";
server.Expect(x => x.MapPath("~/App_Data/foo.txt")).Return(expected);
var requestContext = new RequestContext(context, new RouteData());
sut.ControllerContext = new ControllerContext(requestContext, sut);
// act
var actual = sut.Index();
// assert
var viewResult = actual as ViewResult;
Assert.AreEqual(viewResult.Model, expected);
}
Well I was writing a test today for code that I don't control and they used
private static String GetApplicationPath()
{
return HostingEnvironment.ApplicationVirtualPath.TrimEnd('/');
}
so here is a C# reflection hack to set that value
var path = "/aaaa/bb";
HostingEnvironment hostingEnvironment;
if (HostingEnvironment.IsHosted.isFalse())
new HostingEnvironment();
hostingEnvironment = (HostingEnvironment)typeof(HostingEnvironment).fieldValue("_theHostingEnvironment");
var virtualPath = "System.Web".assembly()
.type("VirtualPath").ctor();
virtualPath.field("_virtualPath", path);
//return virtualPath.prop("VirtualPathString");
//return virtualPath.prop("VirtualPathStringNoTrailingSlash");
hostingEnvironment.field("_appVirtualPath", virtualPath);
//hostingEnvironment.field("_appVirtualPath") == virtualPath;
return HostingEnvironment.ApplicationVirtualPath == path;
//using System.Web.Hosting
It will depend on what mocking or isolation framework you are using. You might want to look into either a) creating a wrapper type around the static property that can be mocked, or b) using a framework which can mock static properties - e.g. Moles or Typemock Isolator
As i faced same issue i changed my code bit.
From
strhtmlTemplate = File.ReadAllText(System.Web.Hosting.HostingEnvironment.MapPath(Lgetfilepath.CVal));
To
strhtmlTemplate = File.ReadAllText(HttpContextFactory.Current.Server.MapPath(Lgetfilepath.CVal));
For Unit test
public HttpContextBase mockHttpContextBase()
{
var moqContext = new Mock<HttpContextBase>();
var moqRequest = new Mock<HttpRequestBase>();
var moqServer = new Mock<HttpServerUtilityBase>();
var moqPath = new Mock<ConfigurationBase>();
moqContext.Setup(x => x.Request).Returns(moqRequest.Object);
moqContext.Setup(x => x.Server.MapPath(#"~\Data\xxxxxxx")).Returns(Environment.CurrentDirectory+#"\xxxxxx");
setupApplication(moqContext);
return moqContext.Object;
}
Now we while Writing TestClass you need to refer above method to mock. Hope it will helpful for your TestCases.
MockDataUT mockData = new MockDataUT();
var mockRequestContext = new HttpRequestContext();
HttpContextFactory.SetCurrentContext(mockData.mockHttpContextBase());
Just use this code..
Make a new folder name Reference in root directory and added your file inside this folder.
Use this
public static XElement GetFile()
{
HttpContext.Current = new HttpContext(new HttpRequest("", "http://www.google.com", ""), new HttpResponse(new StringWriter()));
var doc = new XmlDocument();
var file = HttpContext.Current.Server.MapPath("\\") + "abc.xml";
doc.Load(file);
var e = XElement.Load(new XmlNodeReader(doc));
return e;
}

Can an ASP.NET MVC controller return an Image?

Can I create a Controller that simply returns an image asset?
I would like to route this logic through a controller, whenever a URL such as the following is requested:
www.mywebsite.com/resource/image/topbanner
The controller will look up topbanner.png and send that image directly back to the client.
I've seen examples of this where you have to create a View - I don't want to use a View. I want to do it all with just the Controller.
Is this possible?
Use the base controllers File method.
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");
}
As a note, this seems to be fairly efficient. I did a test where I requested the image through the controller (http://localhost/MyController/Image/MyImage) and through the direct URL (http://localhost/Images/MyImage.jpg) and the results were:
MVC: 7.6 milliseconds per photo
Direct: 6.7 milliseconds per photo
Note: this is the average time of a request. The average was calculated by making thousands of requests on the local machine, so the totals should not include network latency or bandwidth issues.
Using the release version of MVC, here is what I do:
[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");
}
I obviously have some application specific stuff in here regarding the path construction, but the returning of the FileStreamResult is nice and simple.
I did some performance testing in regards to this action against your everyday call to the image (bypassing the controller) and the difference between the averages was only about 3 milliseconds (controller avg was 68ms, non-controller was 65ms).
I had tried some of the other methods mentioned in answers here and the performance hit was much more dramatic... several of the solutions responses were as much as 6x the non-controller (other controllers avg 340ms, non-controller 65ms).
To expland on Dyland's response slightly:
Three classes implement the FileResult class:
System.Web.Mvc.FileResult
System.Web.Mvc.FileContentResult
System.Web.Mvc.FilePathResult
System.Web.Mvc.FileStreamResult
They're all fairly self explanatory:
For file path downloads where the file exists on disk, use FilePathResult - this is the easiest way and avoids you having to use Streams.
For byte[] arrays (akin to Response.BinaryWrite), use FileContentResult.
For byte[] arrays where you want the file to download (content-disposition: attachment), use FileStreamResult in a similar way to below, but with a MemoryStream and using GetBuffer().
For Streams use FileStreamResult. It's called a FileStreamResult but it takes a Stream so I'd guess it works with a MemoryStream.
Below is an example of using the content-disposition technique (not tested):
[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;
}
This might be helpful if you'd like to modify the image before returning it:
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");
}
You can create your own extension and do this way.
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")%>
Why not go simple and use the tilde ~ operator?
public FileResult TopBanner() {
return File("~/Content/images/topbanner.png", "image/png");
}
You can write directly to the response but then it isn't testable. It is preferred to return an ActionResult that has deferred execution. Here is my 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);
}
}
Below code utilizes System.Drawing.Bitmap to load the image.
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();
bitmap.Save(ms, ImageFormat.Jpeg);
ms.Position = 0;
return new FileStreamResult(ms, "image/jpeg");
}
UPDATE: There are better options than my original answer. This works outside of MVC quite well but it's better to stick with the built-in methods of returning image content. See up-voted answers.
You certainly can. Try out these steps:
Load the image from disk in to a byte array
cache the image in the case you expect more requests for the image and don't want the disk I/O (my sample doesn't cache it below)
Change the mime type via the Response.ContentType
Response.BinaryWrite out the image byte array
Here's some sample code:
string pathToFile = #"C:\Documents and Settings\some_path.jpg";
byte[] imageData = File.ReadAllBytes(pathToFile);
Response.ContentType = "image/jpg";
Response.BinaryWrite(imageData);
Hope that helps!
Solution 1: To render an image in a view from an image URL
You can create your own extension method:
public static MvcHtmlString Image(this HtmlHelper helper,string imageUrl)
{
string tag = "<img src='{0}'/>";
tag = string.Format(tag,imageUrl);
return MvcHtmlString.Create(tag);
}
Then use it like:
#Html.Image(#Model.ImagePath);
Solution 2: To render image from database
Create a controller method that returns image data like below
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);
}
}
And use it in a view like:
# { Html.RenderAction("View","Image",new {id=#Model.ImageId})}
To use an image rendered from this actionresult in any HTML, use
<img src="http://something.com/image/view?id={imageid}>
This worked for me.
Since I'm storing images on a SQL Server database.
[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;
}
In the snippet above _db.ImageFiles.Find(uuid) is searching for the image file record in the db (EF context). It returns a FileImage object which is just a custom class I made for the model and then uses it as FileContentResult.
public class FileImage {
public string Uuid { get; set; }
public byte[] Data { get; set; }
public string ContentType { get; set; }
}
you can use File to return a file like View, Content etc
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>"); }
}
Look at ContentResult. This returns a string, but can be used to make your own BinaryResult-like class.
if (!System.IO.File.Exists(filePath))
return SomeHelper.EmptyImageResult(); // preventing JSON GET/POST exception
else
return new FilePathResult(filePath, contentType);
SomeHelper.EmptyImageResult() should return FileResult with existing image (1x1 transparent, for example).
This is easiest way if you have files stored on local drive.
If files are byte[] or stream - then use FileContentResult or FileStreamResult as Dylan suggested.
I see two options:
1) Implement your own IViewEngine and set the ViewEngine property of the Controller you are using to your ImageViewEngine in your desired "image" method.
2) Use a view :-). Just change the content type etc.
You could use the HttpContext.Response and directly write the content to it (WriteFile() might work for you) and then return ContentResult from your action instead of ActionResult.
Disclaimer: I have not tried this, it's based on looking at the available APIs. :-)
I also encountered similar requirement,
So in my case I make a request to Controller with the image folder path, which in return sends back a ImageResult object.
Following code snippet illustrate the work:
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>";
And in the Controller from the the image path I produce the image and return it back to the caller
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();
}
Read the image, convert it to byte[], then return a File() with a content type.
public ActionResult ImageResult(Image image, ImageFormat format, string contentType) {
using (var stream = new MemoryStream())
{
image.Save(stream, format);
return File(stream.ToArray(), contentType);
}
}
}
Here are the usings:
using System.Drawing;
using System.Drawing.Imaging;
using System.IO;
using Microsoft.AspNetCore.Mvc;
Yes you can return Image
public ActionResult GetImage(string imageFileName)
{
var path = Path.Combine(Server.MapPath("/Images"), imageFileName + ".jpg");
return base.File(path, "image/jpeg");
}
(Please don't forget to mark this as answer)
From a byte[] under Core 3.2, you can use:
public ActionResult Img(int? id) {
MemoryStream ms = new MemoryStream(GetBytes(id));
return new FileStreamResult(ms, "image/png");
}

Resources