我正在升级旧版软件(.NET Framework 4.8)以与 API 集成,该 API 以 Base64 编码字符串为所有用户提供图片。我可以获取这个字符串,将其提取到字节数组中,然后创建一个将用于缩略图的图像。当我尝试缩放和压缩图像以确保其大小不超过 10kb 时,问题就出现了。
缩放是通过根据图像大小和最大允许字节 10,000 估计缩放因子来完成的。这不是一个完美的缩放函数,但它是一个足够的估计。 在保存图像之前获取文件的大小很困难,因为使用 bmp.Save() 方法时内存流的大小“不”等于位图的大小。但是,如果我采用相同的字节数组并使用 File.SaveAllBytes() 保存它,则生成的 jpg 的大小与源字节数组的大小完全匹配。 使用 File.SaveAllBytes 方法的缺点是无法控制压缩级别。因此,这个可怕的循环解决方案似乎是保证文件小于 10000 字节的唯一方法。该循环以不同程度的压缩保存临时文件,直到临时文件的大小低于 10000 字节。 当前问题
得到一些大约 10,200 字节大的文件,尽管临时文件表明它应该属于这个大小。如果有人有任何提示,我很想看看如何改进此代码,而不必在 10000 字节限制上添加一些额外的“填充”。 然而,当我尝试将文件限制为 10kb 时,问题就出现了。
这是我当前的解决方案。
using (MemoryStream ms = new MemoryStream(imageByteArray))
{
Bitmap bmp = new Bitmap(ms);
if (bmp != null)
{
long quality = 82;
//SaveImageToFile(bmp, filePath, quality);
long size = ObtainFileSize(bmp, imageName, quality);
double scaleFactor =
Math.Sqrt((double)allowedFileSizeInByte / (double)ms.Length);
int newWidth = (int)(bmp.Width * scaleFactor);
int newHeight = (int)(bmp.Height * scaleFactor);
bmp = new Bitmap(bmp, new Size(newWidth, newHeight));
while (size > allowedFileSizeInByte)
{
quality = quality - 5;
size = ObtainFileSize(bmp, imageName, quality);
}
SaveImageToFile(bmp, filePath, quality);
bmp.Dispose();
//result.Dispose();
}
}
此外,我还有两个用于实际保存的辅助方法。
private void SaveImageToFile(Bitmap bmp, string imagePath, long quality)
{
try
{
//All images will be encoded as ajpg with varyig quality
ImageCodecInfo jgpEncoder = GetEncoder(ImageFormat.Jpeg);
System.Drawing.Imaging.Encoder imageEncoder =
System.Drawing.Imaging.Encoder.Quality;
EncoderParameters myEncoderParameters = new EncoderParameters(1);
myEncoderParameters.Param[0] = new EncoderParameter(imageEncoder, quality);
bmp.Save(imagePath, jgpEncoder, myEncoderParameters);
}
catch
{
throw;
}
}
private long ObtainFileSize(Bitmap bmp, string imageName, long quality)
{
using (Bitmap newBmp = new Bitmap(bmp))
{
// Get the temporary folder path
string tempPath = Path.GetTempPath();
// Combine the path with your file name
string tempFile = Path.Combine(tempPath, imageName + ".jpeg");
// Save the image to the temporary file
SaveImageToFile(newBmp, tempFile, quality);
long size = new FileInfo(tempFile).Length;
File.Delete(tempFile);
return size;
}
}
// for now we only use JPG but this can change...
private ImageCodecInfo GetEncoder(ImageFormat format)
{
ImageCodecInfo[] codecs = ImageCodecInfo.GetImageDecoders();
foreach (ImageCodecInfo codec in codecs)
{
if (codec.FormatID == format.Guid)
{
return codec;
}
}
return null;
}
}
1.重试压缩直到尺寸合适
有一个 API 可以为您执行此操作,或者提供一些其他帮助程序来帮助估计参数。但我不认为任何内置编码器可以做到这一点。您可以对内存流进行编码,以避免访问磁盘,直到找到一些合适的参数。 2.不要使用压缩
File.SaveAllBytes()
的意思。对于任何给定的文件大小,这很可能会导致图像质量变差。
3.使用固定速率压缩