The ImageHandler class is responsible for converting the image to jpg and 60x60 size, then it converts it to Base64. The first Image is fine but when I try to proccess another image in the same run it crashes.
class ImageHandler
{
public static void convertToFormat(string filename)
{
var image = Image.FromFile(#filename);
var bitmap = ResizeImage(image, Globals.ImageSize, Globals.ImageSize);
bitmap.Save(Globals.PRED_PATH, ImageFormat.Jpeg);
}
public static Bitmap ResizeImage(Image image, int width, int height)
{
var destRect = new Rectangle(0, 0, width, height);
var destImage = new Bitmap(width, height);
destImage.SetResolution(image.HorizontalResolution, image.VerticalResolution);
using (var graphics = Graphics.FromImage(destImage))
{
graphics.CompositingMode = CompositingMode.SourceCopy;
graphics.CompositingQuality = CompositingQuality.HighQuality;
graphics.InterpolationMode = InterpolationMode.HighQualityBicubic;
graphics.SmoothingMode = SmoothingMode.HighQuality;
graphics.PixelOffsetMode = PixelOffsetMode.HighQuality;
using (var wrapMode = new ImageAttributes())
{
wrapMode.SetWrapMode(WrapMode.TileFlipXY);
graphics.DrawImage(image, destRect, 0, 0, image.Width, image.Height, GraphicsUnit.Pixel, wrapMode);
}
}
return destImage;
}
public static string ConvertImageToBase64String()
{
Image image = Image.FromFile(Globals.PRED_PATH);
var imageStream = new MemoryStream();
image.Save(imageStream, ImageFormat.Jpeg);
imageStream.Position = 0;
var imageBytes = imageStream.ToArray();
return Convert.ToBase64String(imageBytes);
}
}
Function Call:
ImageHandler.convertToFormat(FilePath);
string encodedImage = ImageHandler.ConvertImageToBase64String();
As a quick fix you can add image.Dispose(); after image.Save(imageStream, ImageFormat.Jpeg); in the method ConvertImageToBase64String to make it work.
However I would recommend that you add using statements to convertToFormat and ConvertImageToBase64String to properly free the resources after use.
public static void convertToFormat(string filename)
{
using (var image = Image.FromFile(#filename))
{
using (var bitmap = ResizeImage(image, Globals.ImageSize, Globals.ImageSize))
{
bitmap.Save(Globals.PRED_PATH, ImageFormat.Jpeg);
}
}
}
public static string ConvertImageToBase64String()
{
using (var image = Image.FromFile(Globals.PRED_PATH))
{
using (var imageStream = new MemoryStream())
{
image.Save(imageStream, ImageFormat.Jpeg);
imageStream.Position = 0;
var imageBytes = imageStream.ToArray();
return Convert.ToBase64String(imageBytes);
}
}
}
Related
I have an ASP.NET Core 3.1 WebAPI that is responsible for providing other apps I have with images. I am trying to improve the performance of my apps. One of the feedback I get from running PageSpeed Insights test is to Serve images in next-gen formats.
All of the images that my WebAPI server are in JPEG format. I don't know much about image compression or image-formats, but I would like to evaluate the results from converting JPEG images to WebP as study show that WebP leads to better results.
I use the following ImageProcessor class to save uploaded images to my WebAPI.
public class ImageProcessor
{
public Image GetResizedImage(Stream stream, int newWidth, out ImageFormat imageFormat)
{
Image sourceImage = Image.FromStream(stream);
SizeF thumbSize = GetNewSize(sourceImage, newWidth);
imageFormat = sourceImage.RawFormat;
return ResizeImage(sourceImage, (int)thumbSize.Width, (int)thumbSize.Height);
}
protected SizeF GetNewSize(Image img, int newMaxWidth)
{
var size = new SizeF
{
Width = newMaxWidth,
Height = (newMaxWidth * img.Height) / img.Width
};
return size;
}
protected Bitmap ResizeImage(Image image, int width, int height)
{
var destRect = new Rectangle(0, 0, width, height);
var destImage = new Bitmap(width, height);
var xRes = Math.Max(Math.Min(image.HorizontalResolution, 96), 72);
var yRes = Math.Max(Math.Min(image.VerticalResolution, 96), 72);
destImage.SetResolution(xRes, yRes);
using (Graphics graphics = Graphics.FromImage(destImage))
{
graphics.CompositingMode = CompositingMode.SourceCopy;
graphics.CompositingQuality = CompositingQuality.HighQuality;
graphics.InterpolationMode = InterpolationMode.HighQualityBicubic;
graphics.SmoothingMode = SmoothingMode.HighQuality;
graphics.PixelOffsetMode = PixelOffsetMode.HighQuality;
using (ImageAttributes wrapMode = new ImageAttributes())
{
wrapMode.SetWrapMode(WrapMode.TileFlipXY);
graphics.DrawImage(image, destRect, 0, 0, image.Width, image.Height, GraphicsUnit.Pixel, wrapMode);
}
}
return destImage;
}
}
Here is how I call the ImageProcessor class to store images on disk from the controller
public async Task<IActionResult> Store(IFormFile file)
{
if(!ModelState.IsValid)
{
return Problem("Invalid model!");
}
// Process Image
Image image = GetResizedImage(file.OpenReadStream(), 1024, out ImageFormat imageFormat);
MemoryStream memoryStream = new MemoryStream();
image.Save(memoryStream, imageFormat);
memoryStream.Seek(0, SeekOrigin.Begin);
// Store it on disk
string fullPath = "full path to the new image";
Directory.CreateDirectory(Path.GetDirectoryName(fullPath));
using FileStream cacheWriter = new FileStream(fullPath, FileMode.OpenOrCreate, FileAccess.Write, FileShare.Write, 4096, true);
await memoryStream.CopyToAsync(cacheWriter);
return Ok();
}
Question
How can I store the image in a WebP format using .Net Core?
If I were you I would look at installing an instance of Thumbor or using Cloudinary to generate your webp files.
Then you can use JS to conditionally load the correct file.
I need to change the size of the photo and save it to a new size when the user selects a photo for the upload, before saving the photo.
i using this code but it not save with new size . whats the problem ?
public async Task<IActionResult> UploadNewsPic()
{
var file = Request.Form.Files[0];
try
{
if (file.Length > 0)
{
string fileName = ContentDispositionHeaderValue.Parse(file.ContentDisposition).FileName.Trim('"');
string fullPath = Path.Combine(_applicationRoot.UploadNewPath(), file.Name);
using (var stream = new FileStream(fullPath, FileMode.Create))
{
if (file.IsImage())
{
await file.ResizeImage(3, 3);
file.CopyTo(stream);
}
}
}
return Ok();
}
catch (Exception e)
{
return BadRequest();
}
}
and this is Resize Extention :
public async static Task<Image> ResizeImage(this IFormFile file, int width, int height)
{
using (var memoryStream = new MemoryStream())
{
await file.CopyToAsync(memoryStream);
using (var img = Image.FromStream(memoryStream))
{
return img.Resize(width, height);
}
}
}
public static Image Resize(this Image image, int width, int height)
{
var res = new Bitmap(width, height);
using (var graphic = Graphics.FromImage(res))
{
graphic.InterpolationMode = InterpolationMode.HighQualityBicubic;
graphic.SmoothingMode = SmoothingMode.HighQuality;
graphic.PixelOffsetMode = PixelOffsetMode.HighQuality;
graphic.CompositingQuality = CompositingQuality.HighQuality;
graphic.DrawImage(image, 0, 0, width, height);
}
return res;
}
You need to save the result from ResizeImage to the stream. Right now you are just copying the original file.
var img = await file.ResizeImage(3, 3);
img.Save(stream, SomeFormat);
Your ResizeImage() function return a resized image, it doesn't edit the image itself, so you must set it to a variable.
if (file.IsImage())
{
Image imageResized = await file.ResizeImage(3, 3);
// ...
}
this is my model class
public class News: Public
{
[Key]
public int NewsID { get; set; }
[Required]
[StringLength(100, MinimumLength = 2)]
public string Title { get; set; }
[DataType(DataType.Date)]
[DisplayFormat(DataFormatString = "{0:yyyy-MM-dd}", ApplyFormatInEditMode = true)]
public DateTime NewsDate { get; set; }
[Required]
[DataType(DataType.MultilineText)]
[StringLength(1000, MinimumLength = 2)]
public string NewsTXT { get; set; }
public byte[] NewsPic { get; set; }
}
I use it in create controller for save picture in Byte[] to database
using (var binaryReader = new BinaryReader(Request.Files[0].InputStream))
{
news.NewsPic = binaryReader.ReadBytes(Request.Files[0].ContentLength);
}
I want make thumb of image in my controller and show in view and don't want save thumb anywhere.
i want do something like this, but image fetch from database
Thumbnail Controller
public ActionResult Thumbnail(int width, int height){
var imageFile = Path.Combine(Server.MapPath("~/app_data"), "test.png");
using (var srcImage = Image.FromFile(imageFile))
using (var newImage = new Bitmap(width, height))
using (var graphics = Graphics.FromImage(newImage))
using (var stream = new MemoryStream())
{
graphics.SmoothingMode = SmoothingMode.AntiAlias;
graphics.InterpolationMode = InterpolationMode.HighQualityBicubic;
graphics.PixelOffsetMode = PixelOffsetMode.HighQuality;
graphics.DrawImage(srcImage, new Rectangle(0, 0, width, height));
newImage.Save(stream, ImageFormat.Png);
return File(stream.ToArray(), "image/png");
}
}
in view
<img src="#Url.Action("Thumbnail", "SomeController", new { width = 100, height = 50 })" alt="thumb" />
my version of this code is, in thumb controller is:
public ActionResult Thumbnail(int width, int height,int id)
{
// TODO: the filename could be passed as argument of course
var photo = db.News.Find(id).NewsPic;
string imageBase64 = Convert.ToBase64String(photo);
var imageSrc = string.Format("data:image/jpg;base64,{0}", imageBase64);
using (var srcImage = Image.FromFile(imageSrc))
using (var newImage = new Bitmap(width, height))
using (var graphics = Graphics.FromImage(newImage))
using (var stream = new MemoryStream())
{
graphics.SmoothingMode = SmoothingMode.AntiAlias;
graphics.InterpolationMode = InterpolationMode.HighQualityBicubic;
graphics.PixelOffsetMode = PixelOffsetMode.HighQuality;
graphics.DrawImage(srcImage, new Rectangle(0, 0, width, height));
newImage.Save(stream, ImageFormat.Png);
return File(stream.ToArray(), "image/png");
}
}
my version, in view:
<img src="#Url.Action("Thumbnail", "News", new { width = 100, height = 50,id=Model.NewsID })" alt="thumb" />
but dosent work
public ActionResult Thumbnail(int width, int height, int id){
// TODO: the filename could be passed as argument of course
var photo = db.News.Find(id).NewsPic;
var base64 = Convert.ToBase64String(photo);
// Convert Base64 String to byte[]
byte[] imageBytes = Convert.FromBase64String(base64);
MemoryStream ms = new MemoryStream(imageBytes, 0, imageBytes.Length);
// Convert byte[] to Image
ms.Write(imageBytes, 0, imageBytes.Length);
Image image = Image.FromStream(ms, true);
using (var newImage = new Bitmap(width, height))
using (var graphics = Graphics.FromImage(newImage))
using (var stream = new MemoryStream())
{
graphics.SmoothingMode = SmoothingMode.AntiAlias;
graphics.InterpolationMode = InterpolationMode.HighQualityBicubic;
graphics.PixelOffsetMode = PixelOffsetMode.HighQuality;
graphics.DrawImage(image, new Rectangle(0, 0, width, height));
newImage.Save(stream, ImageFormat.Png);
return File(stream.ToArray(), "image/png");
}
}
I am trying to generate a thumbnail from the base64string. I am storing the image in a table and am trying to generate a thumbnail from the base64string being stored.
I am able to generate the thumbnail if I provide a path to the image, but that will not work in my case.
This is the working solution of generating a thumbnail from an image path:
protected void GenerateThumbnail(object sender, EventArgs e)
{
string path = Server.MapPath("../src/img/myImage.png");
System.Drawing.Image image = System.Drawing.Image.FromFile(path);
using (System.Drawing.Image thumbnail = image.GetThumbnailImage(100, 100, new System.Drawing.Image.GetThumbnailImageAbort(ThumbnailCallback), IntPtr.Zero))
{
using (MemoryStream memoryStream = new MemoryStream())
{
thumbnail.Save(memoryStream, ImageFormat.Png);
Byte[] bytes = new Byte[memoryStream.Length];
memoryStream.Position = 0;
memoryStream.Read(bytes, 0, (int)bytes.Length);
string base64String = Convert.ToBase64String(bytes, 0, bytes.Length);
Image2.ImageUrl = "data:image/png;base64," + base64String;
Image2.Visible = true;
}
}
}
Can anyone provide any advice on how to use the base64string instead of the image path the generate thumbnail?
Assuming, b64 is the base64 string, you can convert it to a byte array and use that to construct the starting image.
byte[] bytes = Convert.FromBase64String(b64);
using (MemoryStream ms = new MemoryStream(bytes))
{
Bitmap thumb = new Bitmap(100, 100);
using (Image bmp = Image.FromStream(ms))
{
using (Graphics g = Graphics.FromImage(thumb))
{
g.InterpolationMode = InterpolationMode.HighQualityBicubic;
g.CompositingQuality = CompositingQuality.HighQuality;
g.SmoothingMode = SmoothingMode.HighQuality;
g.DrawImage(bmp, 0, 0, 100, 100);
}
}
// a picturebox to show/test the result
picOut.Image = thumb;
}
Be sure to dispose of the thumb when you are done with it.
Mixed few tricks found online. (one from #Plutonix)
string ThumbNailBase64 = ResizeBase64Image(YourBase64String,200, 300);
base64 Input => resize => base64 Output
You get the desired thumbnail with auto aspect ratio.
public static string ResizeBase64Image(string Base64String, int desiredWidth, int desiredHeight)
{
Base64String = Base64String.Replace("data:image/png;base64,", "");
// Convert Base64 String to byte[]
byte[] imageBytes = Convert.FromBase64String(Base64String);
using (MemoryStream ms = new MemoryStream(imageBytes))
{
// Convert byte[] to Image
ms.Write(imageBytes, 0, imageBytes.Length);
Image image = Image.FromStream(ms, true);
var imag = ScaleImage(image, desiredWidth, desiredHeight);
using (MemoryStream ms1 = new MemoryStream())
{
//First Convert Image to byte[]
imag.Save(ms1, imag.RawFormat);
byte[] imageBytes1 = ms1.ToArray();
//Then Convert byte[] to Base64 String
string base64String = Convert.ToBase64String(imageBytes1);
return "data:image/png;base64,"+base64String;
}
}
}
public static Image ScaleImage(Image image, int maxWidth, int maxHeight)
{
var ratioX = (double)maxWidth / image.Width;
var ratioY = (double)maxHeight / image.Height;
var ratio = Math.Min(ratioX, ratioY);
var newWidth = (int)(image.Width * ratio);
var newHeight = (int)(image.Height * ratio);
var newImage = new Bitmap(newWidth, newHeight);
using (var graphics = Graphics.FromImage(newImage))
graphics.DrawImage(image, 0, 0, newWidth, newHeight);
return newImage;
}
I have a simple web application (ASP.NET MVC 5 C#) which allows users to upload several files (images actually).
Currently it works well, the images are stored into the data base and I can read them later.
But I want to resize the images before saving them into the db, since the user can upload extremely big images.
Here is my controller:
public ActionResult Create(Annonce annonce, IEnumerable<HttpPostedFileBase> photos)
{
if (ModelState.IsValid)
{
// Read each uploaded files and add if into the collection
foreach (HttpPostedFileBase fichier in photos)
{
if (fichier != null && fichier.ContentLength > 0)
{
// Making a new object
var photo = new Photo
{
FileName = System.IO.Path.GetFileName(fichier.FileName),
ContentType = fichier.ContentType
};
using (var reader = new System.IO.BinaryReader(fichier.InputStream))
{
photo.Content = reader.ReadBytes(fichier.ContentLength);
}
// Add the current image to the collection
annonce.Photos.Add(photo);
}
}
db.Annonces.Add(annonce);
db.SaveChanges();
return RedirectToAction("Details", new { id = annonce.ID });
}
return View(annonce);
}
How can I resize my images and still be able to save them into the db?
Is-it even possible?
Thanks!
This code will perform a high quality resizing.(means you wont lose very much)
public static Bitmap ResizeImage(Image image, int width, int height)
{
var destRect = new Rectangle(0, 0, width, height);
var destImage = new Bitmap(width, height);
destImage.SetResolution(image.HorizontalResolution, image.VerticalResolution);
using (var graphics = Graphics.FromImage(destImage))
{
graphics.CompositingMode = CompositingMode.SourceCopy;
graphics.CompositingQuality = CompositingQuality.HighQuality;
graphics.InterpolationMode = InterpolationMode.HighQualityBicubic;
graphics.SmoothingMode = SmoothingMode.HighQuality;
graphics.PixelOffsetMode = PixelOffsetMode.HighQuality;
using (var wrapMode = new ImageAttributes())
{
wrapMode.SetWrapMode(WrapMode.TileFlipXY);
graphics.DrawImage(image, destRect, 0, 0, image.Width,image.Height, GraphicsUnit.Pixel, wrapMode);
}
}
return destImage;
}
Call ResizeImage() and assign it to a bitmap which you'll insert into your database.goodluck
you can convert it to byte array and than store it in your db as byte type
public byte[] imageToByteArray(System.Drawing.Image imageIn)
{
MemoryStream ms = new MemoryStream();
imageIn.Save(ms,System.Drawing.Imaging.ImageFormat.Gif);
return ms.ToArray();
}
you can do the same but inverted to get it out and display it as image from the DB:
public Image byteArrayToImage(byte[] byteArrayIn)
{
MemoryStream ms = new MemoryStream(byteArrayIn);
Image returnImage = Image.FromStream(ms);
return returnImage;
}
You also approach to ImageResizer as link below:
http://www.c-sharpcorner.com/article/image-resize-in-asp-net-mvc-using-image-resizer/