ASP.NET アプリケーションで .NET の GDI+ で多くの画像処理を行っています。
Image.FromFile() がファイルハンドルを開いたままにしていることがよくあります。
どうしてこれなの?ファイルハンドルを保持せずに画像を開く最良の方法は何ですか?
- 注意: 私は、Image オブジェクトをそのままにしておくような愚かなことはしていません。
ASP.NET アプリケーションで .NET の GDI+ で多くの画像処理を行っています。
Image.FromFile() がファイルハンドルを開いたままにしていることがよくあります。
どうしてこれなの?ファイルハンドルを保持せずに画像を開く最良の方法は何ですか?
私はこのスレッドの他のいくつかのポスターと同じ旅をしました. 私が注意したこと:
Image.FromFile の使用は、ファイル ハンドルを解放するときに予測できないように見えます。Image.Dispose() を呼び出しても、すべてのケースでファイル ハンドルが解放されませんでした。
FileStream と Image.FromStream メソッドを使用すると機能し、FileStream で Dispose() を呼び出すか、Kris が推奨する Using {} ステートメントですべてをラップすると、ファイルのハンドルが解放されます。ただし、Image オブジェクトをストリームに保存しようとすると、Image.Save メソッドは例外"A general error occurred in GDI+"をスローします。おそらく、Save メソッドの何かが元のファイルについて知りたいと考えています。
スティーブンのアプローチは私にとってはうまくいきました。メモリ内の Image オブジェクトを使用して、元のファイルを削除できました。イメージをストリームとファイルの両方に保存することもできました (これらの両方を行う必要がありました)。元のファイルと同じ名前のファイルに保存することもできました。これは、 Image.FromFile メソッドを使用すると不可能であると文書化されています (確かにこれが最も可能性の高いユースケースであるため、これは奇妙だと思いますが、ちょっと.)
要約すると、次のように画像を開きます。
Image img = Image.FromStream(new MemoryStream(File.ReadAllBytes(path)));
その後、必要に応じてファイル (および元のファイル) を自由に操作できます。
私は同じ問題を抱えており、を使用してファイルを読み取ることに頼っています
return Image.FromStream(new MemoryStream(File.ReadAllBytes(fileName)));
Image.FromFile は、画像が破棄されるまでファイル ハンドルを開いたままにします。MSDNから:
「画像が破棄されるまで、ファイルはロックされたままです。」
Image.FromStream を使用すると、問題は発生しません。
using(var fs = new FileStream(filename, FileMode.Open, FileAccess.Read))
{
return Image.FromStream(fs);
}
編集:(1年と少し後)
上記のコードは予測できないため危険です。ある時点 (ファイルストリームを閉じた後) で、 「GDI+ で一般的なエラーが発生しました」という恐ろしいメッセージが表示される場合があります。私はそれを次のように修正します:
Image tmpImage;
Bitmap returnImage;
using(var fs = new FileStream(filename, FileMode.Open, FileAccess.Read))
{
tmpImage = Image.FromStream(fs);
returnImage = new Bitmap(tmpImage);
tmpImage.Dispose();
}
return returnImage;
また、すべてのヒント(ReadAllBytes、FileStream => FromStream => newBitmap()を使用してコピーを作成するなど)を試しましたが、すべてうまくいきました。しかし、もっと短いものを見つけられるかどうか疑問に思いました。
using (Image temp = Image.FromFile(path))
{
return new Bitmap(temp);
}
元のImageオブジェクトと同様にファイルハンドルを破棄し、元のファイルから独立しているため、エラーなしでストリームまたはファイルに保存できる新しいBitmapオブジェクトを作成するため、も機能しているように見えます。
適切に廃棄していることを確認してください。
using (Image.FromFile("path")) {}
using式はの省略形です
IDisposable obj;
try { }
finally
{
obj.Dispose();
}
Image.Disposeの場合は@Rexで、Dispose()でGdipDisposeImage extern /nativeWin32呼び出しを呼び出します。
IDisposableは、管理されていないリソースを解放するメカニズムとして使用されます(ファイルハンドルはどれですか)
ガベージコレクターに指を向ける必要があります。ガベージコレクションに翻弄されている場合は、そのままにしておくことは実際には問題ではありません。
この男も同様の不満を持っていました...そして彼はファイルから直接ロードするのではなく、FileStreamオブジェクトを使用する回避策を見つけました。
public static Image LoadImageFromFile(string fileName)
{
Image theImage = null;
fileStream = new FileStream(fileName, FileMode.Open, FileAccess.Read);
{
byte[] img;
img = new byte[fileStream.Length];
fileStream.Read(img, 0, img.Length);
fileStream.Close();
theImage = Image.FromStream(new MemoryStream(img));
img = null;
}
..。
完全なハックのようです...
上記のように、Microsoftの回避策は、いくつかの画像がロードされた後にGDI+エラーを引き起こします。スティーブンが上で述べたように私のためのVBソリューションは
picTemp.Image = Image.FromStream(New System.IO.MemoryStream(My.Computer.FileSystem.ReadAllBytes(strFl)))
複数の単一ページの TIFF ファイルを 1 つのマルチパート TIFF 画像にマージしようとしたときに、同じ問題が発生しました。Image.Save()
「Image.SaveAdd()」を使用する必要がありました: https://msdn.microsoft.com/en-us/library/windows/desktop/ms533839%28v=vs.85%29.aspx
私の場合の解決策は、画像の処理が完了したらすぐに、各画像に対して「.Dispose()」を呼び出すことでした。
' Iterate through each single-page source .tiff file
Dim initialTiff As System.Drawing.Image = Nothing
For Each filePath As String In srcFilePaths
Using fs As System.IO.FileStream = File.Open(filePath, FileMode.Open, FileAccess.Read)
If initialTiff Is Nothing Then
' ... Save 1st page of multi-part .TIFF
initialTiff = Image.FromStream(fs)
encoderParams.Param(0) = New EncoderParameter(Encoder.Compression, EncoderValue.CompressionCCITT4)
encoderParams.Param(1) = New EncoderParameter(Encoder.SaveFlag, EncoderValue.MultiFrame)
initialTiff.Save(outputFilePath, encoderInfo, encoderParams)
Else
' ... Save subsequent pages
Dim newTiff As System.Drawing.Image = Image.FromStream(fs)
encoderParams = New EncoderParameters(2)
encoderParams.Param(0) = New EncoderParameter(Encoder.Compression, EncoderValue.CompressionCCITT4)
encoderParams.Param(1) = New EncoderParameter(Encoder.SaveFlag, EncoderValue.FrameDimensionPage)
initialTiff.SaveAdd(newTiff, encoderParams)
newTiff.Dispose()
End If
End Using
Next
' Make sure to close the file
initialTiff.Dispose()