读写bmp的坑

   1.关于bmp头文件,一般都是14bytes的bmp标识+40bytes的bitmap-info,具体的结构体定义网上太多了,我不列出来。这里需要考虑字节对齐就可以了。

  2.读取和写入24位的bmp。这里需要考虑行对齐,我之前写过32位的,想当然以为24位的bmp内容会是w*h*3,然而事实啪啪打脸,事情上没那么简单,这里涉及到行对齐问题。它要求每行需要是4的倍数!所以需要检查一下w*3为4的倍数,如果不是,那就做处理:

int line_size = width * 3;
if(line_size & 3)
{
	line_size += 4;
	line_size &= 0xFFFFFFFC;
}

 希望上面两个记录能给阅者解决读写bmp的问题。如果说的不对,希望留言评论,谢谢大家。

<think>我们之前讨论了GDI+保存文件时因文件被锁定而导致的错误。用户现在想知道除了之前给出的解决方案(克隆、临时文件等)之外,是否有其他保存方法可以避免文件被锁定的现象。 核心问题:使用Bitmap.Save方法直接覆盖已锁定的文件会导致“GDI+ 中发生一般性错误”,那么是否有替代的保存方法可以避免锁定问题? 回答思路: 1. 首先确认直接使用Save方法覆盖的确容易引发此问题,因为GDI+在加载和保存时会对文件进行锁定。 2. 提出替代方案:使用内存流(MemoryStream)作为中转,或者使用文件流(FileStream)并控制文件访问模式。 3. 还可以考虑使用其他图像处理库(如ImageSharp)来避免GDI+的限制,但这可能需要改变代码结构。 具体替代方法: 方法1:使用MemoryStream中转写入(推荐) ```csharp // 将位图保存到内存流,然后再将内存流写入文件 using (MemoryStream memory = new MemoryStream()) { bmp_s.Save(memory, ImageFormat.Png); // 将内存流写入文件(使用FileStream,并指定文件访问模式为写入,同时允许其他进程读取) using (FileStream fs = new FileStream(r1_s_path, FileMode.Create, FileAccess.Write, FileShare.Read)) { memory.WriteTo(fs); } } ``` 方法2:使用FileStream直接保存(控制文件共享模式) ```csharp // 使用FileStream创建文件并指定文件共享模式(允许其他进程读取) using (FileStream fs = new FileStream(r1_s_path, FileMode.Create, FileAccess.Write, FileShare.Read)) { bmp_s.Save(fs, ImageFormat.Png); } ``` 方法3:使用临时文件并替换(之前已提及,这里再强调) 先保存到临时文件,然后删除原文件(如果存在),再将临时文件重命名为目标文件。这种方法在之前的回答中已经给出。 注意:方法1和方法2的关键在于在写入时允许其他进程对文件进行读取(FileShare.Read),这样即使之前有读取锁定,只要不是独占锁定,就可以写入。但是,如果文件已经被当前进程以独占方式锁定(比如之前用new Bitmap(filePath)加载而没有释放),那么仍然无法写入。因此,结合之前的释放资源和克隆操作,再使用这些保存方法会更安全。 因此,完整的建议是: - 在加载图片时使用克隆并立即释放源文件(如之前的解决方案)。 - 在保存时使用MemoryStream中转或FileStream控制共享模式。 另外,如果要覆盖的文件可能被其他程序(如资源管理器)打开,那么上述方法可能还不够,因为其他程序可能以独占方式打开。此时,临时文件替换法是最可靠的。 最后,如果项目允许,可以考虑使用跨平台的图像处理库(如SixLabors.ImageSharp)来完全避开GDI+的限制。例如: ```csharp using SixLabors.ImageSharp; using SixLabors.ImageSharp.Processing; // 保存图片(ImageSharp不会锁定文件) var image = Image.Load(bmp_s); // 注意:这里需要将Bitmap转换为ImageSharp的图像对象,或者直接从流加载 image.Save(r1_s_path); // 保存为PNG ``` 但是,这需要引入新的NuGet包,并可能涉及大量代码修改。 综上所述,推荐使用方法1(内存流)或方法2(带共享模式的FileStream)作为替代保存方法。 根据要求,最后提出相关问题: §§相关问题§§ 1. MemoryStream在图像处理中有哪些优势与潜在风险? 2. FileStream的文件共享模式(FileShare)有哪些选项,各自适用什么场景? 3. 使用ImageSharp等替代库处理图像相比GDI+有哪些主要优势? 4. 为什么临时文件替换法在文件被占用时仍然可行? 5. 如何确保在保存文件时不会因权限问题导致失败? </think>是的,使用 `bmp_s.Save(r1_s_path, ImageFormat.Png)` 直接保存到文件路径确实容易触发文件锁定问题。以下是几种避免此现象的替代保存方法: ### 推荐替代方案(避免文件锁定) #### 1. **使用内存流中转保存** ```csharp using (MemoryStream ms = new MemoryStream()) { bmp_s.Save(ms, ImageFormat.Png); // 先保存到内存流 File.WriteAllBytes(r1_s_path, ms.ToArray()); // 一次性写入文件 } ``` **优势**:完全绕过 GDI+ 的文件锁定机制 **限制**:大文件可能占用过多内存 #### 2. **文件流 + 共享写入模式** ```csharp using (FileStream fs = new FileStream( r1_s_path, FileMode.Create, FileAccess.Write, FileShare.ReadWrite)) // 关键:允许其他进程读取 { bmp_s.Save(fs, ImageFormat.Png); } ``` **原理**:通过 `FileShare.ReadWrite` 声明共享权限 **注意**:仍需确保无其他进程独占锁定 #### 3. **临时文件 + 原子替换** ```csharp string tempPath = Path.GetTempFileName(); // 创建临时文件 bmp_s.Save(tempPath, ImageFormat.Png); // 保存到临时位置 // 原子操作替换 File.Delete(r1_s_path); // 删除原文件(若存在) File.Move(tempPath, r1_s_path); // 移动临时文件 ``` **优势**:Windows 系统支持原子文件移动操作 **可靠性**:行业标准解决方案(如 Chrome/Firefox 更新机制) #### 4. **第三方库替代(ImageSharp)** ```csharp // 需安装 SixLabors.ImageSharp 包 using SixLabors.ImageSharp; // 转换并保存(无文件锁定) using (var image = Image.Load<Rgba32>(bmp_s)) { image.Save(r1_s_path); // 自动识别 PNG 格式 } ``` **优势**:跨平台 + 无 GDI+ 依赖 **性能**:现代 API 效率更高 ### 综合建议方案 ```csharp void SafeImageSave(Bitmap bmp, string path) { string tempPath = Path.Combine( Path.GetDirectoryName(path), Guid.NewGuid() + ".tmp" ); // 保存到临时文件 using (FileStream fs = new FileStream( tempPath, FileMode.Create, FileAccess.Write, FileShare.Read)) { bmp.Save(fs, ImageFormat.Png); } // 原子替换 if (File.Exists(path)) File.Delete(path); File.Move(tempPath, path); } // 调用示例 SafeImageSave(bmp_s, r1_s_path); ``` ### 关键避原则 1. **永远不直接覆盖**:先写临时文件再替换 2. **隔离 GDI+ 操作**:用 `MemoryStream` 或第三方库中转 3. **显式声明共享**:文件流必须指定 `FileShare.Read` 4. **及时释放资源**:所有 `Bitmap` 对象用完立即 `Dispose()`
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值