[WPF] .NET 5 Stream 관리

.NET Core 부터는 System.Drawing.Image를 못 쓰지만 WPF라서 쓰고 있습니다.

오늘 일하다가 System.Drawing.Image의 MemoryStream에 관하여 이슈가 있었습니다.
제가 Cloud에 올라간 Image를 URI를 통해 byte[] 로 받아서 MemoryStream을 using으로 열어 System.Drawing.Image으로 변환한 다음, ViewModel의 ObservableCollection<System.Drawing.Image>에 Add 했습니다. using으로 열었었기 때문에 당연히 method가 종료되고 MemoryStream은 닫혔을 것입니다.

이후 ViewModel의 ObservableCollection를 ListView에 Binding할 때 WPF는 System.Drawing.Image를 System.Windows.Media.ImageSource로 변환해서 Binding 해야하기 때문에 Converter를 사용해야 했습니다. 이 때 value로 들어온 System.Drawing.Image를 다시 MemoryStream을 열어 Image.Save(MemoryStream, ImageFormat.Jpeg) 를 했더니 A generic error occurred in GDI+. 예외가 발생하면서 Save가 되지 않았는데요. 구글링해서 찾아보니 Image가 살아있는동안 Stream을 닫지 마세요 라는 글만 계속해서 검색이 되었습니다. 분명 Converter 안의 로직은 stream이 닫혀지지 않은 채로 Image를 Save했었는데 알고보니 이전에 이 Image에 대하여 MemoryStream을 이미 한 번 열고 닫았기 때문 에 오류가 발생했던 것이 었습니다.

아마도 이건 제가 Stream을 잘 이해하지 못했기 때문에 발생한 것 같습니다. 그냥 단순하게 byte[]나 string이나 image 등등의 객체들을 중간에서 다리 역할을 해주는 가상계층이라고만 생각했었는데 제가 모르는 뭔가가 있었나봅니다.

통상 Stream이라는 것은 한 번쓰고나면 using으로 제깍제깍 처리해버렸는데 Image에 대하여서는 Image가 살아있는 동안은 Stream을 제거하지 않고 Image가 필요없어 졌을 때 Dispose를 실행하면 같이 연결되어 있는 Stream도 함께 제거 된다고 합니다.

이 경우만 본다면, MemoryStream은 일종의 특정 Image에 대한 static처럼 유일한 객체처럼 될 수 있는데요. (stream이 static이 아니라 static처럼 유일하다는 맥락) 이걸 전역으로 편하게 관리할 수 있는 방법이 있을까요? 메서드에서 using으로 사용하지 않더라도 stream을 사용하고 나면 리턴 후 함수에서 사용된 stream은 관리되지 않는 가비지 가 될 것이기 때문입니다.

장문의 글이 되었지만, 혹시 노하우가 있으신 분이 계실까요?

1개의 좋아요

MemoryStream을 전역으로 편하게 관리 할 수 있는 방법”

이란 것이 데이터를 버리지 않고 계속해서 들고 관리하고 싶다라는 것으로 이해 했는데요.,

제대로 이해한 것이 맞다면 단순하게 그냥 "MemoryStream을 ToArray()로 복사본을 들고 있으면 됩니다.

ToArray()는 해당 새롭게 배열을 생성해서 해당 데이터를 완전히 복사해버리기에 스트림이 Close되도 계속 사용할 수 있습니다.

하지만 ToArray()는 데이터 용량이 크거나 빈번하게 처리가 되는 경우
메모리 효율성을 봤을때 좋지 않습니다.

그래서 ArraySegment나 Span를 이용해 원하는 크기 만큼만 참조하여 사용하는 것이 좋습니다.
ArraySegment는 데이터를 복사하지 않고 참조하는 배열의 View같은 역할 입니다.

결론은 데이터의 용량이 크고 빈번한 처리라면 필요한 만큼만 참조해서 쓸 수 있는 ArraySegment나 Span같은 구조체를 사용하시는게 좋고

데이터 용량이 작다면 ToArray()로 복사본을 만들어서 계속해서 들고 있으면 됩니다.

참고 : WPF에서 바인딩을 위한 이미지 스트림 이라면 BitmapImage로 바꿔서 딕셔너리등으로 보관할 수 있습니다.

4개의 좋아요

답변 감사드립니다 ㅎㅎ말씀하신거 기억했는데 여기에 한 번 더 여쭤봤어요~

1개의 좋아요