为什么关闭用于创建 Image 对象的内存流会影响新的 Image 对象?

问题描述 投票:0回答:1

问题:

为什么用于 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}");
}
c# image dispose memorystream
1个回答
0
投票

使用 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();
© www.soinside.com 2019 - 2024. All rights reserved.