WebClient を使用してリモート画像を取得すると、粗い GIF が生成され、PNG+BMP を処理できません
質問
こんにちは!
私は画像を返す Web フォーム プロトタイプ (ImageLaoder.aspx) を作成しています。これは、他の Web フォーム/Web ページのこの単純な例のように使用できるようにします。
<img src="http://www.mydomain.com/ImageLoader.aspx?i=http://images.mydomain.com/img/a.jpg" />
これまでのところ、JPG は問題なく読み込まれますが、GIF はオリジナルに比べて「粗く」見え、BMP と PNG では次の例外が発生します。
System.Runtime.InteropServices.ExternalException:GDI+ で一般的なエラーが発生しました
これまでの私のコードは次のようになります。
protected void Page_Load(object sender, EventArgs e)
{
string l_filePath = Request.QueryString["i"];
System.Drawing.Image l_image = GetImage(l_filePath);
if (l_image != null)
{
System.Drawing.Imaging.ImageFormat l_imageFormat = DetermineImageFormat(l_filePath);
WriteImageAsReponse(l_image, l_imageFormat);
}
}
private System.Drawing.Image GetImage(string filePath)
{
WebClient l_WebClient = new WebClient();
byte[] l_imageBytes = l_WebClient.DownloadData(filePath);
System.Drawing.Image l_image = null;
using (MemoryStream l_MemStream = new MemoryStream(l_imageBytes, 0, l_imageBytes.Length))
{
l_MemStream.Write(l_imageBytes, 0, l_imageBytes.Length);
l_image = System.Drawing.Image.FromStream(l_MemStream, true);
l_MemStream.Close();
}
return l_image;
}
private System.Drawing.Imaging.ImageFormat DetermineImageFormat(string filePath)
{
if (filePath.EndsWith(".jpg", StringComparison.OrdinalIgnoreCase))
return System.Drawing.Imaging.ImageFormat.Jpeg;
else if (filePath.EndsWith(".gif", StringComparison.OrdinalIgnoreCase))
return System.Drawing.Imaging.ImageFormat.Gif;
else if (filePath.EndsWith(".png", StringComparison.OrdinalIgnoreCase))
return System.Drawing.Imaging.ImageFormat.Png;
else
return System.Drawing.Imaging.ImageFormat.Bmp;
}
private void WriteImageAsReponse(System.Drawing.Image image, System.Drawing.Imaging.ImageFormat imageFormat)
{
if (image == null)
return;
System.Drawing.Bitmap l_outputBitMap = new Bitmap(image);
if (imageFormat == System.Drawing.Imaging.ImageFormat.Jpeg)
Response.ContentType = "image/jpg";
else if (imageFormat == System.Drawing.Imaging.ImageFormat.Gif)
Response.ContentType = "image/gif";
else if (imageFormat == System.Drawing.Imaging.ImageFormat.Png)
Response.ContentType = "image/png";
else
Response.ContentType = "image/bmp";
l_outputBitMap.Save(Response.OutputStream, imageFormat);
}
GIF が粗く、PNG と BMP が例外を引き起こす理由について何か考えはありますか?
解決
GetImage メソッドに関するいくつかのポイント:
- Image.FromStream を使用するときは、ストリームを閉じたり破棄したりしないでください。
- ストリーム上で Dispose を (using ステートメントを使用して) 呼び出している場合は、Close を呼び出す必要はありません。
- ストリームに書き込んでいますが、「巻き戻し」はしていないため、私が見る限り、l_imageは実際にはデータを取得しません(Image.FromStreamが位置自体をリセットしない限り)。(gif/jpg デコーダではストリームが巻き戻されるが、bmp/png では巻き戻されないため、エラーが発生する可能性があります。)
- バイト配列を受け取る MemoryStream コンストラクターを使用してみてはいかがでしょうか。
つまり、GetImage メソッドは次のものに置き換えることができると思います。
private Image GetImage(string filePath)
{
WebClient l_WebClient = new WebClient();
byte[] l_imageBytes = l_WebClient.DownloadData(filePath);
MemoryStream l_stream = new MemoryStream(l_imageBytes);
return Image.FromStream(l_stream);
}
さて、もっと重要なことは、そもそもなぜ画像を読み込むのかということです。すでに行っているように、あるいは単に拡張子に基づいてコンテンツ タイプを設定して、ファイル自体を応答として提供してはどうでしょうか?言い換えると、コードはすべて次のようになります。
protected void Page_Load(object sender, EventArgs e)
{
string filePath = Request.QueryString["i"];
string extension = l_filePath.Substring(l_filePath.LastIndexOf('.') + 1);
Response.ContentType = "image/" + extension;
byte[] data = new WebClient.DownloadData(filePath);
Response.OutputStream.Write(data, 0, data.Length);
Response.End();
}
もう少しエラー処理 (「これは適切な拡張子ですか?」を含む) があれば良いのですが、それ以外は問題ないと思います。実際に自分で画像をロードする唯一の利点は、画像が本当にロードされているかどうかを検証できることです。 は ウイルスなどではなく画像です。
編集:ただ興味があるから、そうする正当な理由はありますか? 欲しい 画像リクエストはサーバーを経由しますか?Web ページの作成者はなぜ次のように書くのでしょうか。
<img src="http://www.mydomain.com/ImageLoader.aspx?i=http://images.mydomain.com/img/a.jpg" />
の代わりに
<img src="http://images.mydomain.com/img/a.jpg" />
そこには は それが役立つ理由はいくつかありますが、多くの場合、それは単なる無駄です。