I have created a view jpegImage.cshtml (not the partial view) and it has jpegImageController. Controller has Index method, which is writing an image to Output Stream.
In my main view testimage.cshtml, I am calling the jpegImage view as <img src="jpegImage.cshtml" />, which is not showing any image, while if I call the view by URL like http://<server>/jpegImage/Index, it returns the image.
Is there anything I am missing?
Below is my code
-----------JpegImageController.cs----------------
public ActionResult JpegImage()
{
Random random = new Random();
string s = "";
for (int i = 0; i < 6; i++)
s = String.Concat(s, random.Next(10).ToString());
this.Session["CaptchaImageText"] = s;
CaptchaImage ci = new CaptchaImage(this.Session["CaptchaImageText"].ToString(),200, 40, "Century Schoolbook");
this.Response.Clear();
this.Response.ContentType = "image/jpeg";
// Write the image to the response stream in JPEG format.
ci.Image.Save(this.Response.OutputStream, ImageFormat.Jpeg);
// Dispose of the CAPTCHA image object.
ci.Dispose();
return View();
}
CaptchaImage is class which is generating the image
--------------jpegImage.cshtml------------
#Html.BeginForm()
--------testImage.cshtml--------------
< img src="#Url.Content("~/views/JpegImage/JpegImage.cshtml")" alt="Captcha" />
Delete your jpegImage.cshtml. It is not necessary and kind of a waste to generate.
Change your JpegImageController.cs Action to:
public FileResult JpegImage()
{
Random random = new Random();
string s = "";
for (int i = 0; i < 6; i++)
s = String.Concat(s, random.Next(10).ToString());
Session["CaptchaImageText"] = s;
using (CaptchaImage ci = new CaptchaImage(Session["CaptchaImageText"].ToString(), 200, 40, "Century Schoolbook"))
{
// Write the image to a stream in JPEG format.
var output = new MemoryStream();
ci.Image.Save(output, ImageFormat.Jpeg);
return File(output, "image/jpeg");
}
}
In testImage.cshtml simply use:
<img src="#Url.Action("JpegImage")" alt="Captcha" />
Views like jpegImage.cshtml are not served directly by the web server. That is why your view is not being served when you request it in your image tag:
<img src="jpegImage.cshtml" />
Instead, views are rendered as the result of an action method returning a ViewResult. An action method, in turn, is invoked when you make a request for a URL that is routed to the action method. That is why your request for
http://<server>/jpegImage/Index
does return the image. In other words, your image tag should be
<img src="jpegImage/Index" />
(you can probably leave out the "Index" part, as that is probably configured as a default in your route).
By the way, if all you need is for your action to return an image, you don't need to return a ViewResult or to have a view (.cshtml file) at all. Consider just returning a FileResult:
public ActionResult Image(string id)
{
/* Generate your CaptchaImage and somehow get hold of a stream */
return base.File(steam, "image/jpeg");
}
As I don't know the library you are using for generating the image, I cannot tell you how to get a hold of a Stream, but you can probably figure that out yourself.
Related
I want to access and return a resource image from a DLL /connected project.
(Its a file, with build action of Resource). It is not listed in properties/resource as there are hundreds of them in the folder.
The idea is that I can call an image controller.
public ImageResult Display(string resourcePath){
Uri uri = new Uri("pack://application:,,,/ProjectName;component/Images/Vectors/" + resourcePath, UriKind.Absolute);
// What goes here??
}
The problem is i dont know how to turn the URI into an image, in MVC5.
I want to be able to call it from the view. using the url property of the <img> tag
I think you could try WebClient.DownloadData() method to download the image as byte array from specified URI, then convert it to Base64 format with Convert.ToBase64String() and display it on <img> tag using a string property in the viewmodel as src attribute value, below is an example to display the image:
Viewmodel Example
public class ViewModel
{
// other properties
// used to pass image into src attribute of img tag
public string ImageData { get; set; }
}
Controller Action
public ActionResult Display(string resourcePath)
{
Uri uri = new Uri("pack://application:,,,/ProjectName;component/Images/Vectors/" + resourcePath, UriKind.Absolute);
using (var wc = new System.Net.WebClient())
{
// download URI resource as byte array
byte[] image = wc.DownloadData(uri);
// get image extension
string path = string.Format("{0}{1}{2}{3}", uri.Scheme, Uri.SchemeDelimiter, uri.Authority, uri.AbsolutePath);
string extension = System.IO.Path.GetExtension(path).Replace(".", "");
// assign image to viewmodel property as Base64 string format
var model = new ViewModel();
model.ImageData = string.Format("data:image/{0};base64,{1}", extension, Convert.ToBase64String(image));
return View(model);
}
}
View
#model ViewModel
<img src="#Model.ImageData" ... />
Additional note:
If you already know the extension from the resource URI, you could use it directly instead of using Path.GetExtension, here is an example for JPG format:
model.ImageData = string.Format("data:image/jpg;base64,{0}", Convert.ToBase64String(image));
Related issues:
Image to byte array from a url
MVC How to display a byte array image from model
Be sure to register the pack:// scheme as this won't automatically be registered in an MVC app as it is in a WPF app.
In this example code, Blarn0 is a public property in my model class to ensure that the access to the PackUriHelper.UriSchemePack property isn't optimized away when the code is published in Release configuration. I'm sure one can use discards for this very purpose in later versions of C#.
const string scheme = "pack";
if (!UriParser.IsKnownScheme(scheme))
Blarn0 = PackUriHelper.UriSchemePack;
I upload an image into a table with byte[] format. My problem is, that when I retrieve that on a view, the image won't show up.
Model
{
public byte[] image {get; set;}
}
Controller
public async Task<IActionResult> Create(Profile profile, IFormFile image)
{
if (ModelState.IsValid)
{
using (var memoryStream = new MemoryStream())
{
image.CopyTo(memoryStream);
profile.image = memoryStream.ToArray();
}
_context.Add(image);
await _context.SaveChangesAsync();
return RedirectToAction(nameof(Index));
}
return View(image);
}
View
<img src="#item.image" />
You cannot simply dump a byte array as the source of an HTML image tag. It has to be a URI. That typically means that you need an action that retrieves the image data from the database and returns it as file:
[HttpGet("profileimage")]
public async Task<IActionResult> GetProfileImage(int profileId)
{
var profile = _context.Profiles.FindAsync(profileId);
if (profile?.image == null) return NotFound();
return File(profile.image, "image/jpeg");
}
Then, you can do something like:
<img src="#Url.Action("GetProfileImage", new { profileId = item.Id })" />
Alternatively, you can use a Data URI. However, this will result in the entire image data being included in your HTML document, increasing the overall download time of the document and delaying rendering. Additionally, Data URIs must be Base64 encoded, which effectively increases the image size roughly 1.5 times. For small, simple images, it's not too big of a deal, but you definitely should avoid this approach with larger images. Anyways, doing this would look something like:
<img src="data:image/jpeg;base64,#Convert.ToBase64String(item.image)" />
In my controller I have created an action that returns images from a database. The images are stored as bytearrays, so I'm returning the contents as a FileContentResult.
In the view, I'm linking to this action with:
<img src="#Url.Action("GetImage", new { id=a.Id })"/>
The controller action looks like
public ActionResult GetImage(long id)
{
var article = SolrOperations.GetArticleById(id);
if (article != null && !string.IsNullOrEmpty(article.Image) && !string.IsNullOrEmpty(article.ImageType))
{
var imageBytes = Convert.FromBase64String(article.Image);
return new FileContentResult(imageBytes, article.ImageType);
}
return null;
}
This does not yield the desired behavior (even though the images are displayed), as I need the full link to the image, ie: /getimage/id.jpg instead of just /getimage/id. The reason for this is that I want to use ImageProcessor.Web to further process (and cache) the images by supplying a query string in the image src attribute (fx src="myimage.jpg?filter=greyscale").
If you want to axtend the link you can just extend your controller method signature, like this:
public ActionResult GetImage(long id , string src, string filter)
ASP.NET MVC will bind this values and on your View you can write like this:
<img src="#Url.Action("GetImage", new { id=a.Id, src = "myimage.jpg", filter = "greyscale" })"/>
I'm using MVC 3 and using the AjaxUpload plugin to upload an image using AJAX. I don't want to save the image in the file system, instead save it to the session object and then output the stream to populate an image control on the form? Would anyone know how to do this?
No idea why would ever want to do that (store the file in session) because if you have lots of users uploading their files at the same time, storing those files in the memory of your web server, especially if those files are big, won't make this server last very long. Storing the file on the filesystem is the recommended approach.
But anyway, here's how you could do it (assuming you didn't read or cared about my previous remark):
[HttpPost]
public ActionResult Upload(MyViewModel model)
{
if (!ModelState.IsValid)
{
return View(model);
}
var buffer = new byte[model.File.InputStream];
model.File.InputStream.Read(buffer, 0, buffer.Length);
Session["uploadedFile"] = buffer;
return View(model);
}
where the File property on the view models is a HttpPostedFileBase. Next you could have a controller action which will serve this file:
public ActionResult Image()
{
byte[] buffer = (byte[])Session["uploadedFile"];
return File(buffer, "image/png");
}
and in the view you will have an <img> tag pointing to this action:
<img src="#Url.Action("image")" alt="" />
Now of course the AjaxUpload plugin allows you to upload the file using AJAX, so you don't need to reload the entire page. So in this case your controller action could simply return a JSON object to indicate whether the upload process succeeded and then in the success callback set the src property of the <img> tag to the controller action that will serve the file.
SomeView.cshtml:
<img src="#Url.Action("/Image/Render")" />
ImageController.cs:
public ActionResult Render() {
return File((byte[])Session["Avatar"], "image/jpeg")
}
Some example code. Modify it to whatever you want to do. Not really a good idea to sling an image into a session if lots of users. Better to stick it into a db if short lived, or if long lived, a more permanent storage (filesystem maybe).
public ActionResult UploadImage()
{
foreach (string imageName in Request.Files)
{
HttpPostedFileBase file = Request.Files[imageName];
if (file.ContentLength > 0)
{
BinaryReader br = new BinaryReader(file.InputStream);
byte[] content = br.ReadBytes(file.ContentLength);
Session[imageName] = content; // better to store in a db here
}
}
return View();
}
// return the image (controller action) /mycontroller/ViewImage?imageName=whatever
public FileStreamResult ViewImage(string imageName)
{
byte[] content = (byte[])Session[imageName] ; // where ever your content is stored (ideally something other than session)
MemoryStream ms = new MemoryStream(content);
return new FileStreamResult(ms, "application/octet-stream"); // set content type based on input image, it might be png, jpg, gif etc.,
}
Hope this helps.
I have an ASP.NET MVC application where I am displaying images.
These images could be located on the file system or inside a database. This is fine as I can use Url.Action in my image, call the action on my controller and return the image from the relevant location.
However, I want to be able to support images stored in Amazon S3. In this case, I don't want my controller action to return the image, it should instead generate an image URL for Amazon S3.
Although I could just perform this logic inside my view e.g.
<%if (Model.Images[0].ImageLocation == ImageLocation.AmazonS3) {%>
// render amazon image
I need to ensure that the image exists first.
Essentially I need to pass a size value to my controller so that I can check that the image exists in that size (whether it be in the database, file system or amazon s3). Once I am sure that the image exists, then I return the URL to it.
Hope that makes sense,
Ben
Try the following approach.
A model class for an image tag.
public class ImageModel
{
public String Source { get; set; }
public String Title { get; set; }
}
Helper
public static String Image(this HtmlHelper helper, String source, String title)
{
var builder = new TagBuilder("img");
builder.MergeAttribute("src", source);
builder.MergeAttribute("title", title);
return builder.ToString();
}
View with Model.Images of type IEnumerable<ImageModel>
...
<%= Html.Image(Model.Images[0].Source, Model.Images[0].Title) %>
Action
public ActionResult ActionName(/*whatever*/)
{
// ...
var model = ...;
//...
var model0 = ImageModel();
if (Image0.ImageLocation == ImageLocation.AmazonS3)
model0.Source = "an amazon url";
else
model0.Source = Url.Action("GetImageFromDatabaseOrFileSystem", "MyController", new { Id = Image0.Id });
model0.Title = "some title";
model.Images.Add(model0);
// ...
return View(model);
}
An action is a kind of a pseudo code, however the idea should be clear.
After several iterations I have come up with a workable solution, although I'm still not convinced its the best solution.
Originally I followed Anton's suggestion and just set the image url accordingly within my controller action. This was simple enough with the following code:
products.ForEach(p =>
{
p.Images[0].Url = _mediaService.GetImageUrl(p.Images[0], 200);
});
However, I soon found that this approach did not give me the flexibility I needed. Often I will need to display images of different sizes and I don't want to use properties of my model for this such as Product.FullSizeImageUrl, Product.ThumbnailImageUrl.
As far as "Product" is concerned it only knows about the images that were originally uploaded. It doesn't need to know about how we manipulate and display them, or whether we are caching them in Amazon S3.
In web forms I might use a user control to display product details and then use a repeater control to display images, setting the image urls programatically in code behind.
I found that the use of RenderAction in ASP.NET MVC gave me similar flexibility:
Controller Action:
[ChildActionOnly]
public ActionResult CatalogImage(CatalogImage image, int targetSize)
{
image.Url = _mediaService.GetImageUrl(image, targetSize);
return PartialView(image);
}
Media Service:
public MediaCacheLocation CacheLocation { get; set; }
public string GetImageUrl(CatalogImage image, int targetSize)
{
string imageUrl;
// check image exists
// if not exist, load original image from store (fs or db)
// resize and cache to relevant cache location
switch (this.CacheLocation) {
case MediaCacheLocation.FileSystem:
imageUrl = GetFileSystemImageUrl(image, targetSize);
break;
case MediaCacheLocation.AmazonS3:
imageUrl = GetAmazonS3ImageUrl(image, targetSize);
break;
default:
imageUrl = GetDefaultImageUrl();
break;
}
return imageUrl;
}
Html helper:
public static void RenderCatalogImage(this HtmlHelper helper, CatalogImage src, int size) {
helper.RenderAction("CatalogImage", "Catalog", new { image = src, targetSize = size });
}
Usage:
<%Html.RenderCatalogImage(Model.Images[0], 200); %>
This now gives me the flexibility I require and will support both caching the resized images to disk or saving to Amazon S3.
Could do with some url utility methods to ensure that the generated image URL supports SSL / virtual folders - I am currently using VirtualPathUtility.
Thanks
Ben
You can create a HttpWebRequest to load the image. Check the header in the response, if it's 200 that means it was successful, otherwise something went wrong.