I want to upload an optimized images to Amazon.
So that I have found this article that explains how to optimize an image with a compression level.
The problem is that this example saves the image to disk and I need to save it to Amazon storage.
I have this code:
public static ImageCodecInfo OptimizeImage(Image image, string fileName, long compression, string type)
{
var encoderParams = new EncoderParameters(1)
{
Param = {[0] = new EncoderParameter(Encoder.Quality, compression)}
};
return GetEncoderInfo(type);
}
private static ImageCodecInfo GetEncoderInfo(string mime_type)
{
ImageCodecInfo[] encoders = ImageCodecInfo.GetImageEncoders();
for (int i = 0; i <= encoders.Length; i++)
{
if (encoders[i].MimeType == mime_type)
return encoders[i];
}
return null;
}
I should have a Stream or byte[] object in order to send it to the UploadImageToAmazon method as parameter.
So, I have an Image object and its ImageCodecInfo, how can I convert it to Stream or to byte[]?
Or if you could suggest me how to optimize images and upload them into amazon would be also good.
Thanks.
You can save an Image object to a Stream with Image.Save Method (Stream, ImageFormat)
Edit :
If I recap with the code provided in the example you linked:
private void SaveJpg(Image image, string file_name, long compression)
{
try
{
EncoderParameters encoder_params = new EncoderParameters(1);
encoder_params.Param[0] = new EncoderParameter(
System.Drawing.Imaging.Encoder.Quality, compression);
ImageCodecInfo image_codec_info =
GetEncoderInfo("image/jpeg");
File.Delete(file_name);
using(var imageStream = new Stream()){
// save to stream
image.Save(imageStream, image_codec_info, encoder_params);
// upload
UploadImageToAmazon(imageStream);
}
}
catch (Exception ex)
{
MessageBox.Show("Error saving file '" + file_name +
"'\nTry a different file name.\n" + ex.Message,
"Save Error", MessageBoxButtons.OK,
MessageBoxIcon.Error);
}
}
Related
In my scenario I have 3 or more multi-page tiff images which I need to merge into a single tiff image.
Below is the the code I have tried. It merges in to a single tiff image but only with first page of all tiff images.
private static void MergeTiff(string[] sourceFiles)
{
string[] sa = sourceFiles;
//get the codec for tiff files
ImageCodecInfo info = null;
foreach (ImageCodecInfo ice in ImageCodecInfo.GetImageEncoders())
if (ice.MimeType == "image/tiff")
info = ice;
//use the save encoder
Encoder enc = Encoder.SaveFlag;
EncoderParameters ep = new EncoderParameters(1);
ep.Param[0] = new EncoderParameter(enc, (long)EncoderValue.MultiFrame);
Bitmap pages = null;
int frame = 0;
foreach (string s in sa)
{
if (frame == 0)
{
MemoryStream ms = new MemoryStream(File.ReadAllBytes(Path.Combine(Environment.CurrentDirectory, #"C:\Data_Warehouse\SVNRepository\CD.BNS.W5555.LT45555C.D180306.T113850.Z0101\", s)));
pages = (Bitmap)Image.FromStream(ms);
var appDataPath = #"C:\Data_Warehouse\SVNRepository\Tiffiles\";
var filePath = Path.Combine(appDataPath, Path.GetRandomFileName() + ".tif");
//save the first frame
pages.Save(filePath, info, ep);
}
else
{
//save the intermediate frames
ep.Param[0] = new EncoderParameter(enc, (long)EncoderValue.FrameDimensionPage);
try
{
MemoryStream mss = new MemoryStream(File.ReadAllBytes(Path.Combine(Environment.CurrentDirectory, #"C:\Data_Warehouse\SVNRepository\CD.BNS.W5555.LT45555C.D180306.T113850.Z0101\", s)));
Bitmap bm = (Bitmap)Image.FromStream(mss);
pages.SaveAdd(bm, ep);
}
catch (Exception e)
{
//LogError(e, s);
}
}
if (frame == sa.Length - 1)
{
//flush and close.
ep.Param[0] = new EncoderParameter(enc, (long)EncoderValue.Flush);
pages.SaveAdd(ep);
}
frame++;
}
}
I need to join multiple tiff images with all pages from each tiff image. Please advise!
Thanks
EDIT: Updated from below answer
if (frame == 0)
{
MemoryStream ms = new MemoryStream(File.ReadAllBytes(Path.Combine(Environment.CurrentDirectory, #"C:\OMTest\Working\", s)));
pages = (Bitmap)Image.FromStream(ms);
var appDataPath = #"C:\Data_Warehouse\SVNRepository\Tiffiles\";
var filePath = Path.Combine(appDataPath, Path.GetRandomFileName() + ".tif");
//save the first frame
pages.Save(filePath, info, ep);
//Save the second frame if any
int frameCount1 = pages.GetFrameCount(FrameDimension.Page);
if (frameCount1 > 1)
{
for (int i = 1; i < frameCount1; i++)
{
ep.Param[0] = new EncoderParameter(enc, (long)EncoderValue.FrameDimensionPage);
pages.SelectActiveFrame(FrameDimension.Page, i);
pages.SaveAdd(pages, ep);
}
}
}
else
{
//save the intermediate frames
ep.Param[0] = new EncoderParameter(enc, (long)EncoderValue.FrameDimensionPage);
try
{
MemoryStream mss = new MemoryStream(File.ReadAllBytes(Path.Combine(Environment.CurrentDirectory, #"C:\OMTest\Working\", s)));
Bitmap bm = (Bitmap)Image.FromStream(mss);
int frameCount = bm.GetFrameCount(FrameDimension.Page);
for (int i = 0; i < frameCount; i++)
{
bm.SelectActiveFrame(FrameDimension.Page, i);
pages.SaveAdd(bm, ep);
}
}
catch (Exception e)
{
//LogError(e, s);
}
}
You need to select the active frame to ensure you are getting all pages on the TIFF. In your code you need to get the count of frames and loop through these.
The code in your else block might look something like this:
MemoryStream mss = new MemoryStream(File.ReadAllBytes(Path.Combine(Environment.CurrentDirectory, #"C:\Data_Warehouse\SVNRepository\CD.BNS.W5555.LT45555C.D180306.T113850.Z0101\", s)));
Bitmap bm = (Bitmap)Image.FromStream(mss);
int frameCount = bm.GetFrameCount(FrameDimension.Page);
for(int i=0;i<frameCount;i++){
bm.SelectActiveFrame(FrameDimension.Page, i);
pages.SaveAdd(bm, ep);
}
You may have to tweak it as I haven't tested it.
The given code works great to merge single-page TIFF files into a single multi-page TIFF, however, if there are multi-page TIFF files as sources, it will only merge their first page in the resulting TIFF file: the other ones will be discarded.
Since we couldn't find any working samples that could work around this issue, we ended up coding a small C# helper class, which later became a full-fledged multi-platform console application written in .NET Core 2 and C#. We called the project MergeTIFF and we released the whole source code on GitHub under GNU v3 license, so that everyone else can use it as well; we also released the binaries for Windows and Linux (32-bit and 64-bit).
Here's the relevant excerpt of the C# code:
using System;
using System.Drawing;
using System.Drawing.Imaging;
using System.IO;
namespace MergeTiff.NET
{
/// <summary>
/// A small helper class to handle TIFF files
/// </summary>
public static class TiffHelper
{
/// <summary>
/// Merges multiple TIFF files (including multipage TIFFs) into a single multipage TIFF file.
/// </summary>
public static byte[] MergeTiff(params byte[][] tiffFiles)
{
byte[] tiffMerge = null;
using (var msMerge = new MemoryStream())
{
//get the codec for tiff files
ImageCodecInfo ici = null;
foreach (ImageCodecInfo i in ImageCodecInfo.GetImageEncoders())
if (i.MimeType == "image/tiff")
ici = i;
Encoder enc = Encoder.SaveFlag;
EncoderParameters ep = new EncoderParameters(1);
Bitmap pages = null;
int frame = 0;
foreach (var tiffFile in tiffFiles)
{
using (var imageStream = new MemoryStream(tiffFile))
{
using (Image tiffImage = Image.FromStream(imageStream))
{
foreach (Guid guid in tiffImage.FrameDimensionsList)
{
//create the frame dimension
FrameDimension dimension = new FrameDimension(guid);
//Gets the total number of frames in the .tiff file
int noOfPages = tiffImage.GetFrameCount(dimension);
for (int index = 0; index < noOfPages; index++)
{
FrameDimension currentFrame = new FrameDimension(guid);
tiffImage.SelectActiveFrame(currentFrame, index);
using (MemoryStream tempImg = new MemoryStream())
{
tiffImage.Save(tempImg, ImageFormat.Tiff);
{
if (frame == 0)
{
//save the first frame
pages = (Bitmap)Image.FromStream(tempImg);
ep.Param[0] = new EncoderParameter(enc, (long)EncoderValue.MultiFrame);
pages.Save(msMerge, ici, ep);
}
else
{
//save the intermediate frames
ep.Param[0] = new EncoderParameter(enc, (long)EncoderValue.FrameDimensionPage);
pages.SaveAdd((Bitmap)Image.FromStream(tempImg), ep);
}
}
frame++;
}
}
}
}
}
}
if (frame >0)
{
//flush and close.
ep.Param[0] = new EncoderParameter(enc, (long)EncoderValue.Flush);
pages.SaveAdd(ep);
}
msMerge.Position = 0;
tiffMerge = msMerge.ToArray();
}
return tiffMerge;
}
}
}
For additional info and/or to download it, you can take a look at the following resources that we published to better document the whole project:
MergeTIFF on GitHub
Specifications, dependencies and other info
I am making a function where I pass an Image and that will compress the image and return that image object. Following is my code:
public Image CompressImage(Image img)
{
ImageCodecInfo jpegCodec = null;
EncoderParameter imageQualitysParameter = new EncoderParameter(
System.Drawing.Imaging.Encoder.Quality, 50L);
ImageCodecInfo[] alleCodecs = ImageCodecInfo.GetImageEncoders();
EncoderParameters codecParameter = new EncoderParameters(1);
codecParameter.Param[0] = imageQualitysParameter;
for (int i = 0; i < alleCodecs.Length; i++)
{
if (alleCodecs[i].MimeType == "image/jpeg")
{
jpegCodec = alleCodecs[i];
break;
}
}
Image img1;
using (Stream memory = new MemoryStream())
{
img.Save(memory, jpegCodec, codecParameter);
img1 = Image.FromStream(memory);
memory.Close();
memory.Dispose();
}
return img1;
}
But When I am saving it in the memory stream, it is showing error
An unhandled exception of type 'System.ArgumentException' occurred in EnexolImageConversionApp.exe
Additional information: Parameter is not valid.
on line
img.Save(memory, jpegCodec, codecParameter);
Try this code - it selects proper ImageCodecInfo by comparing ImageFormat.Guid and ImageCodecInfo.FormatID:
using System.Drawing.Imaging;
using System.IO;
public static byte[] SaveImageToByteArray(Image image, int jpegQuality = 90)
{
using (var ms = new MemoryStream())
{
var jpegEncoder = GetEncoder(ImageFormat.Jpeg);
var encoderParameters = new EncoderParameters(1);
encoderParameters.Param[0] = new EncoderParameter(Encoder.Quality, (long)jpegQuality);
image.Save(ms, jpegEncoder, encoderParameters);
return ms.ToArray();
}
}
private static ImageCodecInfo GetEncoder(ImageFormat format)
{
var codecs = ImageCodecInfo.GetImageDecoders();
foreach (ImageCodecInfo codec in codecs)
{
if (codec.FormatID == format.Guid)
{
return codec;
}
}
return null;
}
Last step will be loading image again from received byte array.
For better issue understanding please provide exception details, including stacktrace.
i have use a controller action with use of file uploader and i want to compress the image and compress process perform after save the image, so my problem is : i want save only compress image and delete the orignal one. but this code shows error : file because it is being used by another process
My Code is :
public ActionResult submitgeneralinfo(HttpPostedFileBase file, int? EmployeeId, GeneralInfoViewModel model)
{
var ext = Path.GetExtension(file.FileName);
uniquefilename = Convert.ToString(ID) + ext;
var path = Path.Combine(Server.MapPath("~/Attachements/GeneralInfodp/"), uniquefilename);
var compaths = Path.Combine(Server.MapPath("~/Attachements/GeneralInfodp/"), "com" + uniquefilename);
file.SaveAs(path);
file.InputStream.Dispose();
CompressImage(Image.FromFile(path), 30, compaths);
file.InputStream.Close();
file.InputStream.Dispose();
GC.Collect();
FileInfo file3 = new FileInfo(path);
if (file3.Exists)
{
file3.Delete(); // error :- file because it is being used by another process
}
}
private void CompressImage(Image sourceImage, int imageQuality, string savePath)
{
try
{
//Create an ImageCodecInfo-object for the codec information
ImageCodecInfo jpegCodec = null;
//Set quality factor for compression
EncoderParameter imageQualitysParameter = new EncoderParameter(
System.Drawing.Imaging.Encoder.Quality, imageQuality);
//List all avaible codecs (system wide)
ImageCodecInfo[] alleCodecs = ImageCodecInfo.GetImageEncoders();
EncoderParameters codecParameter = new EncoderParameters(1);
codecParameter.Param[0] = imageQualitysParameter;
//Find and choose JPEG codec
for (int i = 0; i < alleCodecs.Length; i++)
{
if (alleCodecs[i].MimeType == "image/jpeg")
{
jpegCodec = alleCodecs[i];
break;
}
}
//Save compressed image
sourceImage.Save(savePath, jpegCodec, codecParameter);
}
catch (Exception e)
{
}
}
Save image direct in low size with using WebImage
[HttpPost]
public ActionResult Index(HttpPostedFileBase file)
{
WebImage img = new WebImage(file.InputStream);
if (img.Width > 1000)
img.Resize(1000, 1000);
img.Save("path");
return View();
}
I'm creating a temp file and get its path with this code:
public ActionResult Comp(string Link)
{
var sv = Server.MapPath(Link);
int quality = 45;
string tempFile = Path.GetTempFileName();
System.IO.File.Copy(sv, tempFile, true);
using (var myBitmap = Image.FromFile(tempFile))
{
ImageCodecInfo myImageCodecInfo = GetEncoderInfo("image/jpeg");
Encoder myEncoder = Encoder.Quality;
EncoderParameter myEncoderParameter = new EncoderParameter(myEncoder, quality);
EncoderParameters myEncoderParameters = new EncoderParameters(1);
myEncoderParameters.Param[0] = myEncoderParameter;
myBitmap.Save(sv, myImageCodecInfo, myEncoderParameters);
}
System.IO.File.Delete(tempFile);
return RedirectToAction("Index");
}
private static ImageCodecInfo GetEncoderInfo(String mimeType)
{
int j;
ImageCodecInfo[] encoders;
encoders = ImageCodecInfo.GetImageEncoders();
for (j = 0; j < encoders.Length; ++j)
{
if (encoders[j].MimeType == mimeType)
return encoders[j];
}
return null;
}
The Comp function using in image compressor. It get string file path, creating a tmp file in C:\Users\User1\AppData\Local\Temp by getted image in Link, nextly encodede tmp file with setted quality and set tmp file to getted image, finally deleting tmp file. But this code create and delete a temp file in C:\Users\User1\AppData\Local\Temp. I want to create temp file in a specific path because my server not get permission this path. So this code may be as follow:
string tempFile = Path.GetTempFileName("/Content/temp/");
//It gets error that do not have overload method of GetTempFileName
If the problem is that the code does not have permissions in the temp folder, how about simply creating a unique file name in a folder the code does have permissions to? The function Path.GetRandomFileName() will do so. It does not create the file for you, it just create a random file name that you can use in a folder of your choice. To be extra safe, you can first check if the file already exists, but that will be highly unlikely.
Example:
String folderPathThatYouCanWriteTo = "Your Writable Path here";
String fullFilePath = null;
do
{
fullFilePath = String.Format(#"{0}\{1}", folderPathThatYouCanWriteTo, Path.GetRandomFileName());
} while (File.Exists(fullFilePath));
//Now you can use fullFilePath
etc.
i am getting Invalid Parameter Error when calling System.Drawing.Image.Save function.
i google and found a few suggestions but nothing works.
what i am trying to do is that, when i upload an image and if it's lager than 100kb i would like to reduce the image size to half. please help.
System.Drawing.Image FullsizeImage = System.Drawing.Image.FromFile(realpath);
FullsizeImage = System.Drawing.Image.FromFile(realpath);
int fileSize = (int)new System.IO.FileInfo(realpath).Length;
while (fileSize > 100000) //If Larger than 100KB
{
SaveJpeg(realpath, FullsizeImage);
fileSize = (int)new System.IO.FileInfo(realpath).Length;
}
private static ImageCodecInfo GetEncoderInfo(string mimeType)
{
// Get image codecs for all image formats
ImageCodecInfo[] codecs = ImageCodecInfo.GetImageEncoders();
// Find the correct image codec
for (int i = 0; i < codecs.Length; i++)
if (codecs[i].MimeType == mimeType)
return codecs[i];
return null;
}
public static void SaveJpeg(string path, Image img)
{
Image NewImage = img;
img.Dispose();
EncoderParameter qualityParam = new EncoderParameter(Encoder.Quality, 85L);
EncoderParameters encoderParams = new EncoderParameters(1);
encoderParams.Param[0] = qualityParam;
ImageCodecInfo jpegCodec = GetEncoderInfo(GetMimeType(path.Substring(path.LastIndexOf('.'), path.Length - path.LastIndexOf('.'))));
//THE ERROR IS HERE!!!!!!
NewImage.Save(path, jpegCodec, encoderParams);
//THE ERROR IS HERE!!!!!!
}
public static string GetMimeType(string extension)
{
if (extension == null)
{
throw new ArgumentNullException("extension");
}
if (!extension.StartsWith("."))
{
extension = "." + extension;
}
switch (extension.ToLower())
{
#region Big freaking list of mime types
// combination of values from Windows 7 Registry and
// from C:\Windows\System32\inetsrv\config\applicationHost.config
// some added, including .7z and .dat
case ".323": return "text/h323";
// more extension here..
#endregion
default:
// if you have logging, log: "No mime type is registered for extension: " + extension);
return "application/octet-stream";
}
}
EDIT : I modified the code as below, now the image is saving without any exception! Thanks! but another problem here. the file size is not getting reduced. which mean my while loop can never exit. please help and thanks again.
using (MemoryStream ms = new MemoryStream(File.ReadAllBytes(realpath)))
{
using (Image FullsizeImage = Image.FromStream(ms))
{
//code here
int fileSize = (int)new System.IO.FileInfo(realpath).Length;
while (fileSize > 100000) //If Larger than 100KB
{
SaveJpeg(realpath, FullsizeImage, 85L);
fileSize = (int)new System.IO.FileInfo(realpath).Length;
}
}
}
Can someone help me please, my problem is not solved yet :(
Because you're disposing an image object.
public static void SaveJpeg(string path, Image img)
{
Image NewImage = img;
img.Dispose(); <------- Here
...
}
EDIT: Method Image.FromFile file opens a stream and that file wont be closed till your method is not terminated. Try to use MemoryStream.
using (MemoryStream ms = new MemoryStream(File.ReadAllBytes(realPath)))
{
using (Image img = Image.FromStream(ms))
{
ImageCodecInfo myImageCodecInfo;
Encoder myEncoder;
EncoderParameter myEncoderParameter;
EncoderParameters myEncoderParameters;
myImageCodecInfo = GetEncoderInfo("image/jpeg");
myEncoder = Encoder.Quality;
myEncoderParameters = new EncoderParameters(1);
myEncoderParameter = new EncoderParameter(myEncoder, 85L);
myEncoderParameters.Param[0] = myEncoderParameter;
img.Save(realPath, myImageCodecInfo, myEncoderParameters);
}
}