使用BitmapEncoder.CreateForTranscoding写PNG元数据时图片损坏如何解决
问题成因
- 流残留多余数据:你使用
ReadWrite模式直接在原文件流上执行转码操作,编码器写入新的PNG数据后,如果新生成的文件体积小于原文件,原文件尾部残留的旧数据不会被自动清除。PNG格式要求文件必须以标准IEND块结尾,尾部的多余数据会被图像解析器判定为文件结构损坏。 - 流指针位置错误:创建BitmapDecoder完成解码操作后,文件流的读取指针已经移动到文件末尾,后续编码器写入数据时如果没有重置指针到流的起始位置,新数据会直接追加到旧数据后方,直接破坏PNG的文件结构。
修复方案
建议通过内存流中转的方式避免直接操作原文件流,彻底规避流残留和指针问题,修改后的实现逻辑如下:
// 第一步:只读模式打开原文件,读取全部数据到内存流 auto readStream = co_await storageFile.OpenAsync(WS::FileAccessMode::Read); auto memStream = winrt::Windows::Storage::Streams::InMemoryRandomAccessStream(); winrt::Windows::Storage::Streams::RandomAccessStream::CopyAsync(readStream, memStream).get(); readStream.Close(); // 第二步:在内存流上执行解码、转码、写入元数据操作 auto decoder = co_await Windows::Graphics::Imaging::BitmapDecoder::CreateAsync(memStream); auto encoder = co_await Windows::Graphics::Imaging::BitmapEncoder::CreateForTranscoding(memStream, decoder); auto propertySet = winrt::Windows::Graphics::Imaging::BitmapPropertySet(); auto ratingValue = winrt::box_value(L"10"); auto value = winrt::Windows::Graphics::Imaging::BitmapTypedValue(ratingValue, winrt::Windows::Foundation::PropertyType::String); propertySet.Insert(L"/tEXt/{str=Rating}", value); co_await encoder.BitmapProperties().SetPropertiesAsync(propertySet); co_await encoder.FlushAsync(); // 第三步:清空原文件,将内存流中的新数据写入原文件 memStream.Seek(0); auto writeStream = co_await storageFile.OpenAsync(WS::FileAccessMode::ReadWrite); writeStream.Size(0); // 清空原文件所有内容 winrt::Windows::Storage::Streams::RandomAccessStream::CopyAsync(memStream, writeStream).get(); co_await writeStream.FlushAsync(); writeStream.Close(); memStream.Close();
如果不想用内存流中转,也可以在原流操作前先重置指针位置,写完新数据后手动截断流清除残留:
stream = co_await storageFile.OpenAsync(WS::FileAccessMode::ReadWrite); stream.Seek(0); // 重置指针到流起始位置 decoder = co_await Windows::Graphics::Imaging::BitmapDecoder::CreateAsync(stream); encoder = co_await Windows::Graphics::Imaging::BitmapEncoder::CreateForTranscoding(stream, decoder); auto propertySet = winrt::Windows::Graphics::Imaging::BitmapPropertySet(); auto ratingValue = winrt::box_value(L"10"); auto value = winrt::Windows::Graphics::Imaging::BitmapTypedValue(ratingValue, winrt::Windows::Foundation::PropertyType::String); propertySet.Insert(L"/tEXt/{str=Rating}", value); co_await encoder.BitmapProperties().SetPropertiesAsync(propertySet); co_await encoder.FlushAsync(); stream.Size(stream.Position()); // 截断流到当前写入位置,清除尾部残留数据 stream.Close();
内容的提问来源于stack exchange,提问作者gunturu mahesh
相关产品推荐
相关产品推荐

