「ASP.NETで動的に画像を作る」でハマった

| トラックバック(0)

ASP.NET(IIS5.0)において、サーバーサイドで動的(リアルタイム)に作り出した画像をクライアントへ送るには、ImageクラスからResponse.OutputStreamへSaveすればよい。

 

と、考えていた。

たとえばこんな感じ。

[C#]

Image img;

// imgに対する描画は割愛

img.Save(context.Response.OutputStream, ImageFormat.Png);

 

実際に、Visual Studioでデバッグしてみてもちゃんと動作するので、安心してIIS 5.0に持って行ってみると、「GDI+で一般的なエラーが発生しました」というエラーを吐き出す。

いろいろと試してみたところ、MemoryStreamに一度書き出してからOutputStreamへ送り込めば大丈夫。おそらく、原因はOutputStreamがシークと読み取りを禁止している点。ImageクラスのSaveメソッド内で読み返しが行われているのか、CanSeek / CanReadをチェックしているのかは不明ですが、直接書き出すのは無理っぽい。

原因が判れば、OutputStreamへの書き込みを中継するストリームを自作することで、間接的に直書きができるかもしれない、と思ってやってみた。

 

Streamを継承した独自ストリームを作り、CanSeek / CanWrite / CanRead をはじめ、Image.Saveがどの順番でStreamのメソッド・プロパティを呼び出しているのか、を調べてみたところ、次のような結果が出た。

get_Position
get_Length
set_Position
get_Position
Write()

今回は、画像サイズが小さいので、Writeが一度しか呼ばれていないけど、サイズが大きかったりフォーマットによっては複数回の呼び出しがかかるかもしれませぬ。

さて、Response.OutputStreamは前述した通り、CanSeek / CanReadがFalseを返すので、PositionプロパティやLengthプロパティへのアクセスは即例外発生となる。今回のケースでは、ストリームの先頭から書き始めることが既知であるため、これらのプロパティがすべて 0 を返すように偽装し、Write()のみOutputStreamへ送り込むことで直書きに成功♪

中継用のストリームはこんな感じ。

[C#]

class OutputStreamRelay : Stream
{
    public OutputStreamRelay() { return; }

    public override bool CanRead { get { return true; } }
    public override bool CanSeek { get { return true; } }
    public override bool CanWrite { get { return true; } }
    public override void Flush() { }
    public override long Length { get { return 0; } }
    public override long Position { get { return 0; } set { } }
    public override int Read(byte[] buffer, int offset, int count) { return 0; }
    public override long Seek(long offset, SeekOrigin origin) { return 0; }
    public override void SetLength(long value) { }
    public override void Write(byte[] buffer, int offset, int count)
    {
        HttpContext.Current.Response.OutputStream.Write(buffer, offset, count);
    }
}

 

あとは、描画したImageクラスから次のように呼び出すだけでOK。(Imageへの描画部分は省略)

[C#]

Image img;
context.Response.ContentType = "image/png";
img.Save(new OutputStreamRelay(), ImageFormat.Png);

 

トラックバック(0)

トラックバックURL: /mt/mt-tb.cgi/29