为什么用于 ImageA 的 MemoryStream 对象会影响名为 ImageB 的克隆 Image 对象? 我知道微软说,当你从内存流创建图像对象时,你必须在图像的生命周期内保持该流打开。 但我试图从 MemoryStream 创建 ImageA,将 ImageA 克隆到 ImageB,然后处理 ImageA 和用于创建 ImageA 的 MemoryStream。 之后我在使用 ImageB 时遇到错误。
下面的所有代码用于从 3 个 tiff 文件(单页)创建一个新的多页 tiff 图像。 最终得到一张 3 页的 tiff 图像。 从此您将获得...
注意:对于一个小测试来说,这可能看起来有很多代码,但我想给出一个非常好的例子,说明我在哪里获取正在使用的对象。 此示例代码中的很多内容都取自几种不同的方法/函数,我只是更改了一些内容,以真正描绘出我通过导入文件本身所做的一切。
Image img = Image.FromFile(@"c:\test\page001.tif");
MemoryStream ms = new MemoryStream();
List<Image> imagesToAdd = new List<Image>()
{
Image.FromFile(@"c:\test\page002.tif"),
Image.FromFile(@"c:\test\page003.tif")
};
// Create compression encoder parameter
EncoderParameter compressionParam = new EncoderParameter(Encoder.Compression, (long)EncoderValue.CompressionCCITT4);
// Create first page frame parameter
EncoderParameter firstFrameParam = new EncoderParameter(Encoder.SaveFlag, (long)EncoderValue.MultiFrame);
// Create additional pages frame parameter
EncoderParameter additionalFramesParam = new EncoderParameter(Encoder.SaveFlag, (long)EncoderValue.FrameDimensionPage);
// Create color depth parameter
EncoderParameter colorDepthParam = new EncoderParameter(Encoder.ColorDepth, (long)1);
// Create flush parameter
EncoderParameter flushParam = new EncoderParameter(Encoder.SaveFlag, (long)EncoderValue.Flush);
// Create last frame parameter
EncoderParameter lastPageParam = new EncoderParameter(Encoder.SaveFlag, (long)EncoderValue.LastFrame);
// Create first page encoder parameters
EncoderParameters firstFrameParams = new EncoderParameters(2)
{
Param = new EncoderParameter[]
{
compressionParam,
firstFrameParam,
}
};
// Create additional pages encoder parameters
EncoderParameters additionalFrameParams = new EncoderParameters(2)
{
Param = new EncoderParameter[]
{
compressionParam,
additionalFramesParam,
}
};
// Create save to file encoder parameters
EncoderParameters saveToFileParams = new EncoderParameters(2)
{
Param = new EncoderParameter[]
{
compressionParam,
colorDepthParam,
}
};
// Create flush encoder parameters
EncoderParameters flushParams = new EncoderParameters(1)
{
Param = new EncoderParameter[]
{
flushParam,
lastPageParam,
}
};
// Get the tiff image codec
ImageCodecInfo codec = ImageCodecInfo.GetImageEncoders().Where(c => c.MimeType == "image/tiff").First();
// Save the first page to memory stream
img.Save(ms, codec, firstFrameParams);
// Save aditional pages to memory stream
foreach (Image image in imagesToAdd)
img.SaveAdd(image, additionalFrameParams);
// Finalize the new multi-page image
img.SaveAdd(flushParams);
//Image newImg = pageOne.SaveMultiPageImage(imgCol, out MemoryStream ms);
Image newImg = Image.FromStream(ms);
// Cleanup
firstFrameParams.Dispose();
additionalFrameParams.Dispose();
saveToFileParams.Dispose();
flushParam.Dispose();
compressionParam.Dispose();
firstFrameParam.Dispose();
additionalFramesParam.Dispose();
colorDepthParam.Dispose();
flushParam.Dispose();
lastPageParam.Dispose();
下面的代码确实是在另一个地方使用的。我已经制作了所有这一个功能来进行测试,以作为一个很好的例子。下面代码的想法是使用上面代码生成的图像,然后对其进行克隆,删除原始图像和用于创建它的内存流,然后尝试使用新创建的 Image 对象。
问题:处理用于创建 newImg Image 对象的内存流后,从 newImg 克隆的新图像(newImg2)会损坏。
// Try to work with the newImg Image object (no issues)
int pageCount = newImg.GetFrameCount(FrameDimension.Page);
for (int i = 0; i < pageCount; i++)
{
newImg.SelectActiveFrame(FrameDimension.Page, i);
Debug.WriteLine($"BEFORE - Page:{i + 1} W/H: {newImg.Width}/{newImg.Height}");
}
// Create a new Image (newImg2) based on the original Image (newImg)
// then dispose of the original Image object as it's no longer needed
Image newImg2 = (Image)newImg.Clone();
newImg.Dispose();
// This causes issues when dealing with newImg2 going forward
ms.Dispose();
// Try to work with the newImg2 Image object (issues)
pageCount = newImg2.GetFrameCount(FrameDimension.Page);
for (int i = 0; i < pageCount; i++)
{
newImg2.SelectActiveFrame(FrameDimension.Page, i);
Debug.WriteLine($"AFTER - Page:{i + 1} W/H: {newImg2.Width}/{newImg2.Height}");
}
使用 Clone() 就像复制指针或引用。两个图像(原始图像和克隆图像)在原始 MemoryStream 中共享相同的底层内存/图像数据。
因此,处置ms后,保存图像数据的内存被释放并且不再有效。引用此 MemoryStream 的任何其他图像在尝试访问其内容时都会失败。
如果你打算在这里关闭memoryStream,请确保newImg2是一个完全独立的副本。
using (MemoryStream cloneStream = new MemoryStream())
{
newImg.Save(cloneStream, ImageFormat.Tiff);
tempStream.Position = 0;
newImg2 = Image.FromStream(cloneStream);
}
这必须在致电之前完成
ms.Dispose();