ASP.NET MVC 3 Preview Image - asp.net-mvc

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.

Related

MVC Access Resource image

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;

The binary image can't display on view asp.net core 2.x

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)" />

C# MVC: Encoding a png, jpg, or pdf return value to prevent XSS

Suppose I have an C# MVC app which has a controller method that returns one of 3 content types: image png, image jpeg, or application pdf. I have read that it is possible to have images that contain XSS payloads. What would be the best way to Encode/escape these return contents so they aren't vulnerable to XSS? The controller method looks like this:
string contentType = "image/png";
MemoryStream mem = new MemoryStream();
if (ImageFormat == null || ImageFormat == "")
{
image.Save(mem, System.Drawing.Imaging.ImageFormat.Png);
}
else
{
if (ImageFormat.ToUpper() == "PNG") image.Save(mem, System.Drawing.Imaging.ImageFormat.Png);
if (ImageFormat.ToUpper() == "JPEG")
{
image.Save(mem, System.Drawing.Imaging.ImageFormat.Jpeg);
contentType = "image/jpeg";
}
}
mem.Position = 0;
mem.Seek(0, SeekOrigin.Begin);
return this.Image(mem, contentType);
Where Image is defined the following class here:
using …
namespace x.Classes
{
public static class ControllerExtensions
{
public static ImageResult Image(this Controller controller, Stream imageStream, string contentType)
{
return new ImageResult(imageStream, contentType);
}
}
}
And the OutputStream is written to using:
using …
namespace x.Classes
{
public class ImageResult : ActionResult
{
public ImageResult(Stream imageStream, string contentType)
{
if (imageStream == null)
throw new ArgumentNullException("imageStream");
if (contentType == null)
throw new ArgumentNullException("contentType");
this.ImageStream = imageStream;
this.ContentType = contentType;
}
public Stream ImageStream { get; private set; }
public string ContentType { get; private set; }
public override void ExecuteResult(ControllerContext context)
{
if (context == null)
throw new ArgumentNullException("context");
HttpResponseBase response = context.HttpContext.Response;
response.ContentType = this.ContentType;
byte[] buffer = new byte[4096];
while (true)
{
int read = this.ImageStream.Read(buffer, 0, buffer.Length);
if (read == 0)
break;
response.OutputStream.Write(buffer, 0, read);
}
response.End();
}
}
}
Is there a way for me to escape/encode the buffer that is getting written to the OutputStream here:`
response.OutputStream.Write(buffer, 0, read);
To protect against XSS attacks? For example if this were HTML that was being returned:
response.OutputStream.Write(HttpUtility.HtmlEncode(buffer), 0, read);
But we know we are returning a jpeg, pdf, or png which means Html encode won't work here. So what do we use to safely escape/encode an image/pdf?
By the time you have buffer ready, it's too late. The same as with HTML, you want to context-sensitively encode any user input in those files, not the whole thing.
Now, with images this doesn't make much sense in the context of XSS, an image is rendered by an image renderer, and not as html, so there won't be any javascript to be run. The general best practice for uploaded images is to process them on the server and save them as a new image, because this removes all unnecessary things, but it has its risks as well if your processor itself is the target of an attack.
SVG for example is a different beast, SVG can have code in it, as can PDF. But again, PDFs will be open on the client with a PDF viewer, not in the context of the web application even if the PDF viewer is the browser itself (the browser hopefully separates Javascript in the PDF from the web page even if the origin is the same).
But javascript in a PDF can still be an issue for the client. Javascript running in a PDF may do harmful things, the simplest of which is consume client resources (ie. DoS of some sort), or it may try to break out of the PDF context somehow exploiting a viewer vulnerability. So the attack would be that one user uploads a malicious PDF for others to download. I think the best you can do against this is scan uploaded files for malware (which you should do anyway).
If you are generating all of this from user input (images, PDFs), then the libraries you use should take care of properly encoding values so that a malicious user can't inject code in a PDF. When the PDF is already generated, you can't "fix" it anymore, user input is mixed with code.
Also make sure to set the following header in responses (along with the correct Content-Type of course):
X-Content-Type-Options: nosniff
You do not need to encode the images themselves, you need to encode/escape the links to the images.
For example:
Link Title
where image.url.png?logout comes from user input.
You would url encode image.url.png?logout as image.url.png%3Flogout so that it is rendered useless to an attacker.

MvcRazorToPdf save as MemoryStream or Byte[]

I'm using MvcRazorToPdf in a Azure website and create my PDF's and output them in the browser.
Now i'm creating a new function to directly email the PDF as attachment (without output them in the browser).
Does anybody know if it is possible to save the PDF (with MvcRazorToPdf) as a MemoryStream or Byte[]?
I think you can handle this in ResultFilter, I used below code to allow user to download file and prompt for download popup, in this way you can grab all your memory stream and store somewhere to send email afterwords.
public class ActionDownloadAttribute : ActionFilterAttribute
{
public override void OnResultExecuted(ResultExecutedContext filterContext)
{
filterContext.HttpContext.Response.AddHeader("content-disposition", "attachment; filename=" + "Report.pdf");
base.OnResultExecuted(filterContext);
}
}
[ActionDownload]
public ActionResult GeneratePdf()
{
List<Comment> comments = null;
using (var db = new CandidateEntities())
{
comments = db.Comments.ToList();
}
return new PdfActionResult("GeneratePdf", comments);
}
I have implemented something like that. So basically I have not been changing my method to output PDF. What I have done is used restsharp to make request at URL where I get PDF then what you have is in lines of (this is partial code only so you can get idea )
var client = new RestClient(IAPIurl);
var request = new RestRequest(String.Format(IAPIurl_generatePDF, targetID), Method.GET);
RestResponse response = (RestResponse) client.Execute(request);
// Here is your byte array
response.RawBytes
Otherwise you can use my answer from here where I discussed directly returning a file.
Hope this helps!

ASP.NET MVC Dynamically generated image URLs

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.

Resources