Can an ASP.NET MVC controller return an Image? - asp.net

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

Related

ASP.NET MVC Image Uploader for CKEditor?

we have a textarea using CKEditor 4.4 on our admin website where users can edit content. They would like to be able to add images from their computer and have them uploaded automatically to the server for hosting.
I've seen a number of image upload scripts for CKEditor, but they all come with a PHP back-end. Does one exist for ASP.NET MVC 4?
I've seen this post and this one which show server-side controls for WebForms, but haven't been able to find an MVC version that we could drop in, or modify to our tastes.
Is my only option to use one of the existing PHP plugins and rewrite the endpoints as ASP.NET MVC?
Thanks.
Based on Alfonso's WebForms code mentioned in the accepted answer, I ended up using a script similar to this in MVC:
using System.Web;
using System.Web.Mvc;
namespace WebApplication1.Controllers
{
public class CKEditorController : Controller
{
const string basePath = #"D:\CKFinder\ckfinder\userfiles\";
const string baseUrl = #"/ckfinder/userfiles/";
const string scriptTag = "<script type='text/javascript'>window.parent.CKEDITOR.tools.callFunction({0}, '{1}', '{2}')</script>";
public ActionResult Index()
{
var funcNum = 0;
int.TryParse(Request["CKEditorFuncNum"], out funcNum);
if (Request.Files == null || Request.Files.Count < 1)
return BuildReturnScript(funcNum, null, "No file has been sent");
if (!System.IO.Directory.Exists(basePath))
return BuildReturnScript(funcNum, null, "basePath folder doesn't exist");
var receivedFile = Request.Files[0];
var fileName = receivedFile.FileName;
if (string.IsNullOrEmpty(fileName))
{
return BuildReturnScript(funcNum, null, "File name is empty");
}
var sFileName = System.IO.Path.GetFileName(fileName);
var nameWithFullPath = System.IO.Path.Combine(basePath, sFileName);
//Note: you may want to consider using your own naming convention for files, as this is vulnerable to overwrites
//e.g. at the moment if two users uploaded a file called image1.jpg, one would clash with the other.
//In the past, I've used Guid.NewGuid() combined with the file extension to ensure uniqueness.
receivedFile.SaveAs(nameWithFullPath);
var url = baseUrl + sFileName;
return BuildReturnScript(funcNum, url, null);
}
private ContentResult BuildReturnScript(int functionNumber, string url, string errorMessage)
{
return Content(
string.Format(scriptTag, functionNumber, HttpUtility.JavaScriptStringEncode(url ?? ""), HttpUtility.JavaScriptStringEncode(errorMessage ?? "")),
"text/html"
);
}
}
}
These aren't exactly MVC samples, but you can find a sample in VB.Net and C# to handle uploads from CKEditor: https://github.com/AlfonsoML/CKEditorUploader
Pick the code that you want and adjust it to your CMS.
The plugin sends the image asynchronously to the server. As long as you have an ASP.NET MVC/Web Api end point to accept the image and save it to the relavant place/update relevant tables, You should be good. Make sure you return data which your plugin is expecting.
for example, from the demo page you provided, the PHP server page is returning the following string on successful upload of the image
<script type="text/javascript">
window.parent.CKEDITOR.tools.callFunction("92", "\/userfiles\/images\/myImgy.jpg", "");
</script>
In your Web api endpoint, You can use HttpContext.Current.Request.Files collection to look for the posted files.
Try this
Html and JavaScript
<script src="~/Vendors/ckeditor/ckeditor.js"></script>
<script src="~/Vendors/ckeditor/adapters/jquery.js"></script>
<div class="jumbotron">
<textarea name="editor1"></textarea>
<script>
CKEDITOR.replace('editor1', {
uiColor: '#9AB8F3',
filebrowserUploadUrl: '/CkEditorUpload/'
});
</script>
</div>
Controller
using System;
using System.IO;
using System.Web;
using System.Web.Mvc;
namespace ImageUploadCkEditor.Controllers
{
public class CkEditorUploadController : Controller
{
const string filesavepath = "~/Content/Uploads/Ckeditor";
const string baseUrl = #"/Content/Uploads/Ckeditor/";
const string scriptTag = "<script type='text/javascript'>window.parent.CKEDITOR.tools.callFunction({0}, '{1}', '{2}')</script>";
public ActionResult Index()
{
var funcNum = 0;
int.TryParse(Request["CKEditorFuncNum"], out funcNum);
if (Request.Files == null || Request.Files.Count < 1)
return BuildReturnScript(funcNum, null, "No file has been sent");
string fileName = string.Empty;
SaveAttatchedFile(filesavepath, Request, ref fileName);
var url = baseUrl + fileName;
return BuildReturnScript(funcNum, url, null);
}
private ContentResult BuildReturnScript(int functionNumber, string url, string errorMessage)
{
return Content(
string.Format(scriptTag, functionNumber, HttpUtility.JavaScriptStringEncode(url ?? ""), HttpUtility.JavaScriptStringEncode(errorMessage ?? "")),
"text/html"
);
}
private void SaveAttatchedFile(string filepath, HttpRequestBase Request, ref string fileName)
{
for (int i = 0; i < Request.Files.Count; i++)
{
var file = Request.Files[i];
if (file != null && file.ContentLength > 0)
{
fileName = Path.GetFileName(file.FileName);
string targetPath = Server.MapPath(filepath);
if (!Directory.Exists(targetPath))
{
Directory.CreateDirectory(targetPath);
}
fileName = Guid.NewGuid() + fileName;
string fileSavePath = Path.Combine(targetPath, fileName);
file.SaveAs(fileSavePath);
}
}
}
}
}

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") %>" ...

MVC3 Valums Ajax File Upload

I'm trying to use valums ajax uploader. http://valums.com/ajax-upload/
I have the following on my page:
var button = $('#fileUpload')[0];
var uploader = new qq.FileUploader({
element: button,
allowedExtensions: ['jpg', 'jpeg', 'png', 'gif'],
sizeLimit: 2147483647, // max size
action: '/Admin/Home/Upload',
multiple: false
});
it does post to my controller but qqfile is always null. I tried these:
public ActionResult Upload(HttpPostedFile qqfile)
AND
HttpPostedFileBase file = Request.Files["file"];
without any luck.
I found an example for ruby on rails but not sure how to implement it in MVC
http://www.jigsawboys.com/2010/10/06/ruby-on-rails-ajax-file-upload-with-valum/
In firebug i see this:
http://localhost:61143/Admin/Home/Upload?qqfile=2glonglonglongname+-+Copy.gif
I figured it out. this works in IE and Mozilla.
[HttpPost]
public ActionResult FileUpload(string qqfile)
{
var path = #"C:\\Temp\\100\\";
var file = string.Empty;
try
{
var stream = Request.InputStream;
if (String.IsNullOrEmpty(Request["qqfile"]))
{
// IE
HttpPostedFileBase postedFile = Request.Files[0];
stream = postedFile.InputStream;
file = Path.Combine(path, System.IO.Path.GetFileName(Request.Files[0].FileName));
}
else
{
//Webkit, Mozilla
file = Path.Combine(path, qqfile);
}
var buffer = new byte[stream.Length];
stream.Read(buffer, 0, buffer.Length);
System.IO.File.WriteAllBytes(file, buffer);
}
catch (Exception ex)
{
return Json(new { success = false, message = ex.Message }, "application/json");
}
return Json(new { success = true }, "text/html");
}
This component is sending an application/octet-stream instead of multipart/form-data which is what the default model binder can work with. So you cannot expect Request.Files to have any value with such a request.
You will need to manually read the request stream:
public ActionResult Upload(string qqfile)
{
var stream = Request.InputStream;
var buffer = new byte[stream.Length];
stream.Read(buffer, 0, buffer.Length);
var path = Server.MapPath("~/App_Data");
var file = Path.Combine(path, qqfile);
File.WriteAllBytes(file, buffer);
// TODO: Return whatever the upload control expects as response
}
IE uploads using multipart-mime. Other browsers use Octet-Stream.
I wrote an upload handler to work with Valums Ajax Uploader that works with both MVC & Webforms & both upload methods. I'd be happy to share with you if you wanted. It closely follows the the PHP handler.
My controller to handle the upload looks like this:
public class UploadController : Controller
{
private IUploadService _Service;
public UploadController()
: this(null)
{
}
public UploadController(IUploadService service)
{
_Service = service ?? new UploadService();
}
public ActionResult File()
{
return Content(_Service.Upload().ToString());
}
The UploadService looks this:
public class UploadService : IUploadService
{
private readonly qq.FileUploader _Uploader;
public UploadService()
: this(null)
{ }
public UploadService(IAccountService accountservice)
{
_Uploader = new qq.FileUploader();
}
public UploadResult Upload()
{
qq.UploadResult result = _Uploader.HandleUpload();
if (!result.Success)
return new UploadResult(result.Error);
.... code .....
return new UploadResult((Guid)cmd.Parameters["#id"].Value);
}
catch (Exception ex)
{
return new UploadResult(System.Web.HttpUtility.HtmlEncode(ex.Message));
}
finally
{
............code.........
}
}
...............code ............
You should try:
Stream inputStream = (context.Request.Files.Count > 0) ? context.Request.Files[0].InputStream : context.Request.InputStream;
I am developing in ASP.Net 4.0 but we don't have MVC architecture. I had same issue few days back. But, I figured it out and here is my solution.
//For IE Browser
HttpPostedFile selectedfile = Request.Files[0];
System.Drawing.Bitmap obj = new System.Drawing.Bitmap(selectedfile.InputStream);
//For Non IE Browser
System.Drawing.Bitmap obj = new System.Drawing.Bitmap(Request.InputStream);
Now, you can use obj for further operation.

Displaying a image from a database in asp.net mvc

I have a view which contains a users id and an image column.
Here's what i've tried doing to retrieve the image but i keep getting a box with an red x instead of the actual image.
View
<td><img src="<%= Url.Action( "DisplayImage" , "User" , new { id = item.id} ) %>" alt="" /></td>
Controller
public FileContentResult DisplayImage(string id)
{
byte[] image = repository.GetImage(id);
return File(image, "image/jpg");
}
i've also tried returning an ActionResult instead and that didn't work either.
Repository
public Byte[] GetImage(string id)
{
var image = db.GetImage(id).First<GetImageResult>();
if (image == null)
return null;
return image.UserImage;
}
LinqTOSQL Class
[Function(Name="dbo.GetImage")]
public ISingleResult<GetImageResult> GetImage([Parameter(DbType="VarChar(8)")] string id)
{
IExecuteResult result = this.ExecuteMethodCall(this, ((MethodInfo)(MethodInfo.GetCurrentMethod())), id);
return ((ISingleResult<GetImageResult>)(result.ReturnValue));
}
public partial class GetImageResult
{
private System.Byte[] _userImage;
public GetImageResult()
{
}
[Column(Storage="_userImage", DbType="Image")]
public System.Byte[] UserImage
{
get
{
return this._userImage;
}
set
{
if ((this. _userImage!= value))
{
this. _userImage = value;
}
}
}
}
I've been killing myself all day trying to get this to work, but it just isn't working.
The return type on the stored procedure is an integer (atleast when i look at parameters in
SQL Server Management Studio it says integer), but i can't redefine that now can i?
It's actually hitting the DisplayImage Action with the correct parameters within the UserController and returning File(imageByteArray, "image/jpg") but only a box with red x is being displayed.
Any help would be greatly appreciated.
edit: I've tried debugging by adding a Reponse.BinaryWrite(imageByteArray) within the action result and hitting the url directly by goign to http://localhost/User/DisplayImage?id=10101010 and the image for that user is displayed in mspaint.
edit2: I also did a view source and my html for that image tag came out as following.
<td>
<img src='/User.mvc/GetImage?id=U00915441' alt="" />
</td>
Thanks
Look at this question I had from a while back - the solution was special ActionResult type for images
Edit: Here's my code. I'm actually creating an ImageResult class from an Image that I created with GDI+ like this :
return new ImageResult()
{
ImageFormat = spriteInfo.ImageFormat,
EncodedImageBytes = spriteInfo.GetImageStream()
};
The image result class is. You'll notice if I provide an EncodedImageBytes parameter it will send that to the output stream. This looks like exactly what you want. On the other hand if you're just passing in an Image then it will just write that Image out to the output stream.
public class ImageResult : ActionResult
{
public ImageResult() { }
public int? Quality { get; set; }
public Image Image { get; set; }
public ImageFormat ImageFormat { get; set; }
public byte[] EncodedImageBytes { get; set; }
public override void ExecuteResult(ControllerContext context)
{
// verify properties
if (EncodedImageBytes == null)
{
if (Image == null)
{
throw new ArgumentNullException("Image");
}
}
if (ImageFormat == null)
{
throw new ArgumentNullException("ImageFormat");
}
// output
context.HttpContext.Response.Clear();
if (ImageFormat.Equals(ImageFormat.Bmp)) context.HttpContext.Response.ContentType = "image/bmp";
if (ImageFormat.Equals(ImageFormat.Gif)) context.HttpContext.Response.ContentType = "image/gif";
if (ImageFormat.Equals(ImageFormat.Icon)) context.HttpContext.Response.ContentType = "image/vnd.microsoft.icon";
if (ImageFormat.Equals(ImageFormat.Jpeg)) context.HttpContext.Response.ContentType = "image/jpeg";
if (ImageFormat.Equals(ImageFormat.Png)) context.HttpContext.Response.ContentType = "image/png";
if (ImageFormat.Equals(ImageFormat.Tiff)) context.HttpContext.Response.ContentType = "image/tiff";
if (ImageFormat.Equals(ImageFormat.Wmf)) context.HttpContext.Response.ContentType = "image/wmf";
// output stream
Stream outputStream = context.HttpContext.Response.OutputStream;
if (EncodedImageBytes != null)
{
outputStream.Write(EncodedImageBytes, 0, EncodedImageBytes.Length);
}
else
{
ImageUtil.SaveImageToStream(outputStream, Image, ImageFormat, Quality);
}
}
}

Resources