项目中有一个需求,就是要导出一个达到几百兆超大图片文件。
因为Bitmap类是有大小限制的,而且内存也不允许。
所以去探索了一下bitmap存储的原理,想通过自己构造一个bmp来实现超大文件的导出。
其实Bitmap就是一个位图,前面54个字节存储文件的基本信息。后面的字节就是存储颜色信息。
实现的思路就是:
1.创建一个空的test.bmp文件,并写入54个头文件字节
2.事先计算好最终图片的高度和宽度
3.根据最终图片的高度和宽度计算出来 最终的字节数,例如1000个字节,则直接new byte[1000],写入test.bmp
4.获取每一个小bitmap的颜色字节,填充到第3步的字节中。
头文件信息说明:
在实现之前。先理解一下Bitmap的存储(以下针对RBG24),你可以把它看做是一个矩形,展示出来的时候就是拿取颜色信息,按照位置放到矩形中去。
它是一个位映射的,所以如果多个bitmap写入到一个总bitmap中的时候,存储的byte位置是不连续的
附上代码:
FileStream fs=new FileStream("G:\\bytesFile\\12.GDSS", FileMode.Open, FileAccess.ReadWrite);
byte[] bytes = new byte[100 * 1440*3];
fs.Read(bytes, 0, bytes.Length);
int totalImage = (int)(fs.Length / (1440 * 3)); //总张数
int startCount = (int)(totalImage/1000);
int forCount = totalImage % 1000 == 0 ? startCount : startCount + 1; //循环次数
//随便先拿一个bitmap的头文件信息,先写入流,后面修改
Bitmap bitmap = Helper.GetBitmapByBytes(bytes, 1440 * 3, false);
FileStream write = new FileStream("G:\\bytesFile\\lyy12.bmp", FileMode.OpenOrCreate, FileAccess.ReadWrite);
MemoryStream ms = new MemoryStream();
bitmap.Save(ms, ImageFormat.Bmp);
byte[] soure=ms.ToArray();
write.Write(soure, 0, 54);
List<int> lstWidth = new List<int>();
List<long> lstByteCount = new List<long>();
long allLength = 0;
long perRowLength = 0;
//空白填充
for (int j = 0; j < 1440; j++)
{
for (int i = 0; i < forCount; i++)
{
int c = (totalImage - (i * 1000) >= 1000) ? 1000 : totalImage - (i * 1000);
lstWidth.Add(c);
byte[] fill;
if (c % 4 == 0)
{
fill = new byte[c * 3];
}
else
{
fill = new byte[(c * 3) + (4 - (c * 3 % 4))];
}
if (i == 0) write.Seek(54+j* perRowLength, SeekOrigin.Begin);
else write.Seek(j* perRowLength+54 + lstWidth[i - 1], SeekOrigin.Begin);
allLength += fill.Length;
if (j == 0)
{
lstByteCount.Add(fill.Length);
perRowLength += fill.Length;
}
write.Write(fill, 0, fill.Length);
}
}
//修改文件大小
write.Seek(2, SeekOrigin.Begin);
long len = 54 + allLength;
write.Write(BitConverter.GetBytes(len));
//修改文件宽度
write.Seek(18, SeekOrigin.Begin);
write.Write(BitConverter.GetBytes(totalImage));
//设置文件高度
write.Seek(22, SeekOrigin.Begin);
write.Write(BitConverter.GetBytes(1440));
FileStream txt = new FileStream("G:\\log.txt",FileMode.OpenOrCreate,FileAccess.ReadWrite);
StreamWriter sw = new StreamWriter(txt);
int w = 0;
//颜色填充
for (int i = 1; i <= forCount; i++)
{
int c = (totalImage - ((i - 1) * 1000) >= 1000) ? 1000 : totalImage - ((i - 1) * 1000);
long pos = (i - 1) * 1000 * 1440 * 3;
fs.Seek(pos, SeekOrigin.Begin);
byte[] result = new byte[c * 1440 * 3];
fs.Read(result, 0, result.Length);
using (Bitmap resultBitmap = Helper.GetBitmapByBytes(result, 1440 * 3, false))
{
//resultBitmap.Save($"G:\\images\\{i}.bmp", ImageFormat.Bmp);
w += resultBitmap.Width;
using (MemoryStream msResult = new MemoryStream())
{
msResult.Flush();
resultBitmap.Save(msResult, ImageFormat.Bmp);
byte[] temp = msResult.ToArray();
for (int h = 0; h < 1440; h++)
{
write.Flush();
byte[] rowByte = new byte[lstByteCount[i - 1]];
Array.Copy(temp, 54 + h * lstByteCount[i - 1], rowByte, 0, rowByte.Length);
long kk = 54 + (h * perRowLength) + lstByteCount.Take(i - 1).Sum();
sw.WriteLine($"第{i}张---->{h}---->{kk}");
write.Seek(kk, SeekOrigin.Begin);
write.Write(rowByte, 0, rowByte.Length);
rowByte = null;
}
temp = null;
}
}
}
txt.Close();
// sw.Dispose();
fs.Close();
write.Close();
fs.Dispose();
write.Dispose();
MessageBox.Show("ok");
Helper.cs
public static Bitmap GetBitmapByBytes(byte[] bytes, int stride, bool mirror)
{
if (bytes.Length == 0) return null;
int height = bytes.Length / stride;
int width = 1440;
Bitmap bitmap = new Bitmap(width, height, PixelFormat.Format24bppRgb);
BitmapData bmpData = bitmap.LockBits(new System.Drawing.Rectangle(0, 0, bitmap.Width, bitmap.Height), ImageLockMode.ReadWrite, bitmap.PixelFormat);
//得到一个指向Bitmap的buffer指针
IntPtr ptrBmp = bmpData.Scan0;
//int nImageStride = 1440 * 3;
//图像宽能够被4整除直接copy
for (int i = 0; i < bitmap.Height; ++i)
{
Marshal.Copy(bytes, i * stride, new IntPtr(ptrBmp.ToInt64() + i * bmpData.Stride), 1440 * 3);
}
//BitmapData解锁
bitmap.UnlockBits(bmpData);
bitmap.RotateFlip(RotateFlipType.Rotate270FlipX);
return bitmap;// BitmapHelper.CompressImage(bitmap);
}
花了我两天时间研究,总算是可以出来图片了。但是!!!!当文件大小超过300M,会出现前后重复,中间空白的情况,如下图,小于300M的就正常。不清楚什么原因。这个问题还未解决,有知道的可告知一下,感谢~~~