Convert IFormFile to Image in Asp Core - c#

i need to resize file upload if file is image .
i write the extention for resize that :
public static Image ResizeImage(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;
}
and this is Upload Action :
[HttpPost("UploadNewsPic"), DisableRequestSizeLimit]
public IActionResult UploadNewsPic(IFormFile file)
{
if (file.IsImage())
{
}
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))
{
file.CopyTo(stream);
}
}
return Ok();
}
catch (Exception e)
{
return BadRequest();
}
}
now my problem is here => my extention just work on type of Image file but type of this file is IFormFile . how can i convert the IFormFile to Image type ?

You should use the Image.FromStream() method to read the stream as an Image:
public async Task<IActionResult> FileUpload(IFormFile file)
{
if (file == null || file.Length == 0)
{
return BadRequest();
}
using (var memoryStream = new MemoryStream())
{
await file.CopyToAsync(memoryStream);
using (var img = Image.FromStream(memoryStream))
{
// TODO: ResizeImage(img, 100, 100);
}
}
}

You need to open file using OpenReadStream and convert into image format. And pass the same to your extension method.
FileDetails fileDetails;
using (var reader = new StreamReader(file.OpenReadStream()))
{
var fileContent = reader.ReadToEnd();
var parsedContentDisposition = ContentDispositionHeaderValue.Parse(file.ContentDisposition);
fileDetails = new FileDetails
{
Filename = parsedContentDisposition.FileName,
Content = fileContent,
ContentType=file.ContentType
};
}

Related

ASP.Net Core 5 MVC - Default image from wwwroot folder if no image is selected on a form

How does one set a default image in a cshtml form if no image is selected?
I have tried to do an "...If... ...Else..." statement in the controller, but it doesn't seem to be working.
Here is the current code snippet I have:
public async Task<IActionResult> Create(PetReg petReg, List<IFormFile> Pet1ChipImage, byte[] bytes)
{
foreach (var item in Pet1ChipImage)
{
if (item.Length > 0)
{
using (var stream = new MemoryStream())
{
await item.CopyToAsync(stream);
petReg.Pet1ChipImage = stream.ToArray();
}
}
else
{
string contenttype = string.Empty;
string filename = string.Empty;
filename = "no-image.png";
contenttype = "image/png";
Image defaultImage = Image.FromFile(MyServer.MapPath("~/Images/Default.png"));
using (MemoryStream ms = new MemoryStream())
{
await item.CopyToAsync(ms);
bytes = ms.ToArray();
}
}
}

ASP.NET Core 5 web api duplicates file name on save

I have an API written in asp.net core 5. There I have a controller that receive a IFile object. Everything is fine till I need on the end of the process to save the file on the local hard drive. There my path named formatedPath is correct and it contains the full path (like: ‪D:/tanulmanyiversenyek.hu/server/profile/8226dfa5-2743aiE2-45cfpTjG-b186yENr-167280a987aa.png).
But when the save is called I god an exception with error message:
D:\tanulmanyiversenyek.hu\server\profile of the filename ‪D:/tanulmanyiversenyek.hu/server/profile/8226dfa5-2743aiE2-45cfpTjG-b186yENr-167280a987aa.png
The bolder part is added somehow. I can't figure out why.
[Route("api/v{version:apiVersion}/image/profile")]
[ApiVersion(ApplicationSettingsConstans.ActiveVersion)]
[HttpPost]
[AllowAnonymous]
[ProducesResponseType((int)HttpResponseType.OK, Type = typeof(string))]
[ProducesResponseType((int)HttpResponseType.BadRequest)]
[Produces("application/json")]
public async Task<string> UploadProfileImageAsync([FromForm] IFormFile file)
{
ServiceResponse<string> request = await _imageUploadService.UploadProfileImageAsync(file);
if (!request.IsSuccess)
{
throw new Exception(request.Message);
}
return request.ResultObject;
}
public async Task<ServiceResponse<string>> UploadProfileImageAsync(IFormFile file)
{
ServiceResponse<string> response = new ServiceResponse<string>();
try
{
response.ResultObject = await ImageUploadHandler.SaveProfileImageAsync(file);
}
catch (Exception ex)
{
response.IsSuccess = false;
response.Message = ex.Message;
response.ResultObject = string.Empty;
}
return response;
}
public static async Task<string> SaveProfileImageAsync(IFormFile file)
{
if (file.Length < 1)
{
return null;
}
string result = string.Empty;
string extension = file.FileName.Reverse().Split(".")[0].Reverse();
string fileName = $"{GenerateName()}.{extension}";
string subFolder = UploadedFileType.Profile.ToString().ToLower();
#if DEBUG
string path = $#"{ServerPathEnviorement.Base()}\{subFolder}\{fileName}";
result = $#"\{subFolder}\{fileName}";
#else
string path = $#"{ServerPathEnviorement.Base()}/{subFolder}/{fileName}";
result = $#"/{subFolder}/{fileName}";
#endif
await SaveOnDisk(path, file, 720, 405);
return result;
}
private static async Task SaveOnDisk(string path, IFormFile file, int width = 1920, int height = 1080)
{
string formatedPath = path.Replace(#"/", #"\");
Image image = null;
using (MemoryStream memoryStream = new MemoryStream())
{
await file.CopyToAsync(memoryStream);
image = Image.FromStream(memoryStream);
}
using (Bitmap bitmap = new Bitmap(image))
{
byte[] data = ImageCompressor.ScaleImage(bitmap, width, height, false);
using (MemoryStream memoryStream = new MemoryStream(data))
{
image = Image.FromStream(memoryStream);
image.Save(formatedPath, ImageFormat.Jpeg); *//error thrown here*
}
}
The same error appears form Postman or from my react app.
Interesting think is if I repleace the formatedPath variable with direct path to file like: d:/tanulmanyiversenyek.hu/server/profile/167280a987aa.png it is working !

C# Image Resize from Stream

I'm trying to resize an image from stream. Getting the error
Value cannot be null.Parameter name: encoder
on the line
System.Drawing.Bitmap fullSizeBitmap = new
System.Drawing.Bitmap(fullsizeImage, new System.Drawing.Size(width,
image_height));
How do I add an encoder here? and I need it to be from the original image
public static FileProperty UploadImage(IFormFile file, string folderPath, string fileName, FileNote note, int image_height)
{
FileProperty property = new FileProperty();
if (file.Length > 0)
{
MemoryStream ms = new MemoryStream();
file.CopyTo(ms);
var fileBytes = ms.ToArray();
MemoryStream inputMemoryStream = new MemoryStream(fileBytes);
System.Drawing.Image fullsizeImage = System.Drawing.Image.FromStream(inputMemoryStream);
int width = (image_height / fullsizeImage.Height) * fullsizeImage.Width;
System.Drawing.Bitmap fullSizeBitmap = new System.Drawing.Bitmap(fullsizeImage, new System.Drawing.Size(width, image_height));
using (var stream = new MemoryStream())
{
fullSizeBitmap.Save(stream, fullSizeBitmap.RawFormat);
using (MemoryStream memoryStream = new MemoryStream(stream.ToArray()))
{
UploadFromStreamAsync(memoryStream);
}
}
property.FileName = fileName;
property.FileExtention = Path.GetExtension(fileName);
property.FileSize = file.Length;
property.FileType = file.ContentType;
property.FileNote = note.ToString();
}
return property;
}
Just use file.OpenReadStream() to read and resize the file, then save the resized bitmap to the MemoryStream and upload.
And instead of using bitmap.RawFormat you can get the uploaded image format using a method as below:
if (file.Length > 0)
{
using(var stream = file.OpenReadStream())
{
var image = Image.FromStream(stream);
int width = (image_height / fullsizeImage.Height) * fullsizeImage.Width;
Bitmap fullSizeBitmap = new Bitmap(fullsizeImage, new Size(width, image_height));
var imgFormat = GetImageFormat(file.FileName);
using(var ms = new MemoryStream())
{
fullSizeBitmap.Save(ms, imgFormat);
UploadFromStreamAsync(ms);
}
}
}
The method to get the image format by file extension:
public ImageFormat GetImageFormat(string fileName)
{
var dotIndex = fileName.LastIndexOf('.');
var ext = fileName.Substring(dotIndex, fileName.Length - dotIndex).ToLower();
switch (ext)
{
case ".bmp": return ImageFormat.Bmp;
case ".emf": return ImageFormat.Emf;
case ".exif": return ImageFormat.Exif;
case ".gif": return ImageFormat.Gif;
case ".icon": return ImageFormat.Icon;
case ".Jpg": return ImageFormat.Jpeg;
case ".Jpeg": return ImageFormat.Jpeg;
case ".png": return ImageFormat.Png;
case ".tiff": return ImageFormat.Tiff;
case ".Wmf": return ImageFormat.Wmf;
default: throw new InvalidDataException("Format not supported");
}
}
If you still need to get the uploaded image encoder info use the below method:
// ext: image extension
using System.Drawing.Imaging;
public static ImageCodecInfo GetEncoderInfo(string ext)
{
return ImageCodecInfo.GetImageEncoders().SingleOrDefault(x => x.FilenameExtension.ToLower().Contains(ext));
}

Crashes, OutOfMemoryException, generic error when using System.Drawing

I am running a cron job in C# that takes 200,000 images and convert into 1 bit image. During running this job, sometimes the process crashes (even though I have global try catch), sometimes for some images(not all) it throws OutOfMemoryException and sometimes for some images it it throws A generic error occurred in GDI.
int pageSize = 1000;
for (int pageNumber = 0; pageNumber < 200; pageNumber++)
{
var imageUrls = allIMageUrls.Skip(pageSize * pageNumber).Take(pageSize).ToList();
var counter = 0;
var total = imageUrls.Count;
Logger.Log($"Page Number : {pageNumber}");
var failedImageUrls = new System.Collections.Concurrent.ConcurrentBag<string>();
Parallel.ForEach(imageUrls, imageUrl =>
{
try
{
Interlocked.Increment(ref counter);
var image = _httpService.DownloadImage(imageUrl);
if (image != null && image.Length > 0)
{
var oneBitImage = ConvertToOnebitFaxGroup4(contract);
_httpService.UploadImage(image, oneBitImage);
oneBitImage = null;
image = null;
}
}
catch (Exception ex)
{
failedImageUrls.Add(imageUrl);
Logger.Log(ex);
}
});
This is one time process. I added paging so that when it crashes I can restart from that page instead of start at beginning.
public static class ImageProcessor
{
static ImageCodecInfo _codecInfo;
static EncoderParameters _encoderParameters;
static ImageProcessor()
{
foreach (var codec in ImageCodecInfo.GetImageEncoders())
{
if (codec.MimeType == "image/tiff")
{
_codecInfo = codec;
break;
}
}
_encoderParameters = new EncoderParameters(2);
_encoderParameters.Param[0] = new EncoderParameter(Encoder.Compression, (long)EncoderValue.CompressionCCITT4);
_encoderParameters.Param[1] = new EncoderParameter(Encoder.ColorDepth, (long)1);
}
public static byte[] ConvertToOnebitFaxGroup4(byte[] bytes)
{
using (var memoryStream = new MemoryStream(bytes))
{
var image = Image.FromStream(memoryStream);
var pData = Marshal.AllocHGlobal(bytes.Length);
Marshal.Copy(bytes, 0, pData, bytes.Length);
var bytesPerLine = (image.Width + 31) / 32 * 4;
var img = new Bitmap(image.Width, image.Height, bytesPerLine, PixelFormat.Format1bppIndexed, pData);
using (var ms = new MemoryStream())
{
image.Save(ms, _codecInfo, _encoderParameters);
img.Dispose();
Marshal.FreeHGlobal(pData);
return ms.ToArray();
}
}
}
Updated:
public static byte[] ConvertToOnebitFaxGroup4(byte[] bytes)
{
using (var memoryStream = new MemoryStream(bytes))
{
using (var image = Image.FromStream(memoryStream))
{
var pData = Marshal.AllocHGlobal(bytes.Length);
Marshal.Copy(bytes, 0, pData, bytes.Length);
var bytesPerLine = (image.Width + 31) / 32 * 4;
using (var img = new Bitmap(image.Width, image.Height, bytesPerLine, PixelFormat.Format1bppIndexed, pData))
{
using (var ms = new MemoryStream())
{
img.Save(ms, _codecInfo, _encoderParameters);
Marshal.FreeHGlobal(pData);
return ms.ToArray();
}
}
}
}
}
Update2
public static byte[] ConvertToOnebitFaxGroup4(byte[] bytes)
{
using (var memoryStream = new MemoryStream(bytes))
{
using (var image = Image.FromStream(memoryStream))
{
using (var ms = new MemoryStream())
{
image.Save(ms, _codecInfo, _encoderParameters);
return ms.ToArray();
}
}
}
}

Writing on image to convert image to read only

I have written a handler and a function to send watermark response back. But some how the image changes to read only.
I have disposed all the object.
The codes are as below:
public void ProcessRequest(HttpContext context)
{
var imagePath = QueryString.getValueOf("ID");
var watermark = QueryString.getValueOf("watermark");
context.Response.ContentType = "image/jpeg";
if (string.IsNullOrWhiteSpace(imagePath) || string.IsNullOrWhiteSpace(watermark))
{
var originalImage = Image.FromFile(context.Server.MapPath("Images/NoImage.jpg"));
originalImage.Save(context.Response.OutputStream, ImageFormat.Jpeg);
originalImage.Dispose();
}
else
{
if (watermark == "0")
{
var absolutePath = context.Server.MapPath(imagePath);
var fileexist = System.IO.File.Exists(absolutePath);
if (fileexist)
{
var originalImage = Image.FromFile(absolutePath);
originalImage.Save(context.Response.OutputStream, ImageFormat.Jpeg);
originalImage.Dispose();
}
else
{
var originalImage = Image.FromFile(context.Server.MapPath("Images/NoImage.jpg"));
originalImage.Save(context.Response.OutputStream, ImageFormat.Jpeg);
originalImage.Dispose();
}
}
else
{
if (imagePath.ToUpper().Contains(".GIF"))
{
var absolutePath = context.Server.MapPath(imagePath);
var fileexist = System.IO.File.Exists(absolutePath);
if (fileexist)
{
var originalImage = Image.FromFile(absolutePath);
originalImage.Save(context.Response.OutputStream, ImageFormat.Jpeg);
originalImage.Dispose();
}
else
{
var originalImage = Image.FromFile(context.Server.MapPath("Images/NoImage.jpg"));
originalImage.Save(context.Response.OutputStream, ImageFormat.Jpeg);
originalImage.Dispose();
}
}
else
{
var absolutePath = context.Server.MapPath(imagePath);
var fileexist = System.IO.File.Exists(absolutePath);
if (fileexist)
{
var originalImage = Image.FromFile(absolutePath);
originalImage = new ImageMethods().AddWatermarkText(originalImage);
originalImage.Save(context.Response.OutputStream, ImageFormat.Jpeg);
originalImage.Dispose();
}
else
{
var originalImage = Image.FromFile(context.Server.MapPath("Images/NoImage.jpg"));
originalImage.Save(context.Response.OutputStream, ImageFormat.Jpeg);
originalImage.Dispose();
}
}
}
}
}
and the function is to writing images is as follows
public Image AddWatermarkText(Image img)
{
try
{
var textOnImage = ConfigurationManager.AppSettings["textOnImage"];
var opacity = Int32.Parse(ConfigurationManager.AppSettings["opicity"]);
var red = Int32.Parse(ConfigurationManager.AppSettings["red"]);
var green = Int32.Parse(ConfigurationManager.AppSettings["green"]);
var blue = Int32.Parse(ConfigurationManager.AppSettings["blue"]);
var fontSize = Int32.Parse(ConfigurationManager.AppSettings["fontSize"]);
var fontName = ConfigurationManager.AppSettings["fontName"];
var lobFromImage = Graphics.FromImage(img);
var lobFont = new Font(fontName, fontSize, FontStyle.Regular);
var lintTextHw = lobFromImage.MeasureString(textOnImage, lobFont);
var lintTextOnImageWidth = (int)lintTextHw.Width;
var lintTextOnImageHeight = (int)lintTextHw.Height;
var lobSolidBrush = new SolidBrush(Color.FromArgb(opacity, Color.FromArgb(red, green, blue)));
var posLeft = (img.Width - lintTextOnImageWidth) / 2;
posLeft = posLeft > 0 ? posLeft : 5;
var lobPoint = new Point(posLeft, (img.Height / 2) - (lintTextOnImageHeight / 2));
// var lobPoint = new Point(RandomNumber(0, img.Width - lintTextOnImageWidth), RandomNumber(0, img.Height - lintTextOnImageHeight));
lobFromImage.DrawString(textOnImage, lobFont, lobSolidBrush, lobPoint);
lobFromImage.Dispose();
lobSolidBrush.Dispose();
lobFont.Dispose();
}
catch (Exception ex)
{
HavException = true;
ExceptionMessage = ex.Message;
}
return img;
}
Am I missing something?
if you have an exception before your Dispose call then your images won't be disposed and files will remain locked. You should put calls to Dispose in "finally" section or use "using" like:
using (var originalImage = Image.FromFile(absolutePath))
originalImage.Save(context.Response.OutputStream, ImageFormat.Jpeg);
Also to avoid locking you can read file to stream first and then create image from stream
using(var imageStream = new FileStream(fileName, FileMode.Open, FileAccess.Read))
return Image.FromStream(imageStream );
I solved my problem. I don't know why it worked but it is working.
public void ProcessRequest(HttpContext context)
{
var imagePath = QueryString.getValueOf("ID");
var watermark = QueryString.getValueOf("watermark");
string lstrResponseType;
// context.Response.ContentType = "image/jpeg";
if (string.IsNullOrWhiteSpace(imagePath) || string.IsNullOrWhiteSpace(watermark))
{
using (var originalImage = Image.FromFile(context.Server.MapPath("Images/NoImage.jpg")))
{
originalImage.Save(context.Response.OutputStream, ImageFormat.Jpeg);
originalImage.Dispose();
}
lstrResponseType = "image/jpeg";
}
else
{
var absolutePath = context.Server.MapPath(imagePath);
var fileexist = System.IO.File.Exists(absolutePath);
if (!fileexist)
{
var originalImage = Image.FromFile(context.Server.MapPath("Images/NoImage.jpg"));
originalImage.Save(context.Response.OutputStream, ImageFormat.Jpeg);
lstrResponseType = "image/jpeg";
originalImage.Dispose();
}
else
{
using (var originalImage = Image.FromFile(absolutePath))
{
if (watermark == "0")
{
originalImage.Save(context.Response.OutputStream, ImageFormat.Jpeg);
lstrResponseType = "image/jpeg";
}
else
{
if (imagePath.ToUpper().Contains(".GIF"))
{
originalImage.Save(context.Response.OutputStream, ImageFormat.Gif);
lstrResponseType = "image/gif";
}
else
{
new ImageMethods().AddWatermarkText(originalImage);
originalImage.Save(context.Response.OutputStream, ImageFormat.Jpeg);
lstrResponseType = "image/jpeg";
}
}
}
}
}
context.Response.ContentType = lstrResponseType;
}

Categories

Resources