如何通过流从任意文件创建图片?Image.Save内存不足异常排查
问题分析与解决
错误原因
Image.FromFile仅能加载**符合标准图像格式(如PNG、JPG、BMP等)**的文件,你传入的redo.none是任意非图像文件,GDI+无法将其解析为有效的Image对象,后续调用Save时抛出的“内存不足异常”是误导性报错——实际并非内存不足,而是图像数据无效。
正确实现思路
要将任意文件转成图片,核心是把文件的二进制数据编码到合法的图像结构中,而非直接用Image类加载非图像文件。以下是两种可行方案:
方案一:将文件数据映射为像素图像
把文件的每个字节值作为像素灰度值,生成对应尺寸的灰度图,适合可视化文件数据:
byte[] fileData = File.ReadAllBytes("redo.none"); // 计算图像尺寸,确保总像素数≥文件字节数 int width = (int)Math.Ceiling(Math.Sqrt(fileData.Length)); int height = width; // 用LockBits提升大文件处理效率,避免SetPixel的性能问题 using (Bitmap bmp = new Bitmap(width, height)) { BitmapData bmpData = bmp.LockBits(new Rectangle(0, 0, width, height), ImageLockMode.WriteOnly, PixelFormat.Format8bppIndexed); // 设置灰度调色板 ColorPalette palette = bmp.Palette; for (int i = 0; i < 256; i++) { palette.Entries[i] = Color.FromArgb(i, i, i); } bmp.Palette = palette; // 复制文件数据到图像内存 Marshal.Copy(fileData, 0, bmpData.Scan0, fileData.Length); bmp.UnlockBits(bmpData); bmp.Save("blyad.png"); }
方案二:将文件数据嵌入PNG元数据
如果不需要可视化,仅需把文件数据隐藏在图片中,可将其存入PNG的附加元数据块:
byte[] fileData = File.ReadAllBytes("redo.none"); using (Bitmap bmp = new Bitmap(1, 1)) // 创建最小尺寸的空白图像 { // 创建自定义属性存储文件数据 PropertyItem propItem = bmp.PropertyItems[0]; propItem.Id = 0x8000; // 自定义属性ID(避免冲突) propItem.Type = 1; // 字节数组类型 propItem.Len = fileData.Length; propItem.Value = fileData; bmp.SetPropertyItem(propItem); bmp.Save("blyad.png"); }
大文件场景优化(结合MemoryMappedFile)
如果文件超大,避免一次性加载全部数据到内存,可使用MemoryMappedFile分块读取并处理:
using (var mmf = MemoryMappedFile.CreateFromFile("redo.none", FileMode.Open)) { long fileLength = new FileInfo("redo.none").Length; int width = (int)Math.Ceiling(Math.Sqrt(fileLength)); int height = width; using (Bitmap bmp = new Bitmap(width, height, PixelFormat.Format8bppIndexed)) { // 设置灰度调色板 ColorPalette palette = bmp.Palette; for (int i = 0; i < 256; i++) { palette.Entries[i] = Color.FromArgb(i, i, i); } bmp.Palette = palette; BitmapData bmpData = bmp.LockBits(new Rectangle(0, 0, width, height), ImageLockMode.WriteOnly, PixelFormat.Format8bppIndexed); long offset = 0; int chunkSize = 4096; // 每次读取4KB块 while (offset < fileLength) { int bytesToRead = (int)Math.Min(chunkSize, fileLength - offset); using (var stream = mmf.CreateViewStream(offset, bytesToRead)) { byte[] chunk = new byte[bytesToRead]; stream.Read(chunk, 0, bytesToRead); Marshal.Copy(chunk, 0, IntPtr.Add(bmpData.Scan0, (int)offset), bytesToRead); } offset += chunkSize; } bmp.UnlockBits(bmpData); bmp.Save("blyad.png"); } }
内容的提问来源于stack exchange,提问作者Jut Rut
相关产品推荐
相关产品推荐

