私のアプリケーションには「エクスポート」機能があります。機能面では、次のように機能します。
ユーザーが(オプションなどを構成した後)[エクスポート]ボタンを押すと、アプリケーションはまず、エクスポートする必要があるすべてのオブジェクトのIDを決定する比較的迅速なクエリを実行します。次に、オブジェクトごとに、比較的長い時間がかかる計算を実行します(オブジェクトごとに最大1秒)。これが発生している間、ユーザーは進行状況バーを監視しています。これは、予想されるオブジェクトの数と、これまでに処理されたオブジェクトの数がわかっているため、簡単にレンダリングできます。
通常の理由で、この機能をWebサービスに移行したいと思います。ただし、このプロセスのもう1つのしわは、ユーザーが多くのネットワーク待機時間を持っていることが多いことです。したがって、処理する行が1000行ある場合、1000リクエストを実行する余裕はありません。
私がしたいのは、サービスからカスタムストリームを返すことです。行数をストリームの最初の4バイトに書き込むことができます。クライアントは、これらの4バイトを読み取り、進行状況バーを初期化してから、ストリームの読み取りとその場での逆シリアル化に進み、進行状況バーをそれぞれ逆シリアル化するときに更新します。その間、サーバーは、オブジェクトが利用可能になると、ストリームにオブジェクトを書き込みます。
さらに興味深いことに、オブジェクトの長いリストを送り返すので、オーバーヘッドを減らすためにprotobuf-netを使用したいと思います。したがって、私はいくつかの質問があります:
私がやろうとしていることは可能ですか?それは意味がありますか、それとももっと良い方法がありますか?
ServiceStackサービスからカスタムストリームを返すにはどうすればよいですか?
クライアント側でオブジェクトのストリームを逆シリアル化しているときに、各オブジェクトが逆シリアル化されるときに、ある種の通知をどのように取得できますか?プログレスバーを更新するために必要です。
私はこの答えを見つけました。これは私が望んでいることの一種ですが、私の質問には正確に対応していません:
Lazy, stream driven object serialization with protobuf-net編集:私は私のクライアントがServiceStackとprotobuf.netを使用するデスクトップC#アプリケーションであることを述べたはずです。
カスタムレスポンス、カスタムシリアル化、およびカスタムクライアントがストリーミングレスポンスを消費する必要がある結果のストリームを返そうとするのではなく、複数のリクエストにわたって結果セットをページングする(つまり、スキップ/テイクを使用する)ことをお勧めします。これは、ステートレスなアプローチであり、HTTP経由で各クエリを個別にキャッシュできるため、再試行のサポートが向上します。つまり、リクエストの1つにエラーがあった場合、最後に成功した応答から再試行できます(つまり、ダウンロードする必要はありません)リクエスト全体)が改善され、既存のHTTPツールによるデバッグ性とイントロスペクションが向上しました。
カスタムストリーミング応答
以下は、Observable StreamWriterとカスタムのObservableクライアントを返し、ストリーミングされた応答を消費する方法を示す例です。
https://gist.github.com/bamboo/5078236カスタムJSONシリアル化を使用して、各要素がストリームにフラッシュされる前に確実に書き込まれるようにして、ストリームを消費するクライアントが各読み取りでレコード全体を取得できるようにします。プロトコルバッファーのようなバイナリシリアライザーを使用する場合、このカスタムシリアル化はより困難になります。
ServiceStackでバイナリとストリームの応答を返す
ImageServiceは、ServiceStackでバイナリまたはストリーム応答を返すさまざまな方法を示しています。
HttpResultでストリームを返す
public object Any(ImageAsStream request)
{
using (var image = new Bitmap(100, 100))
{
using (var g = Graphics.FromImage(image))
{
g.Clear(request.Format.ToImageColor());
}
var ms = new MemoryStream();
image.Save(ms, request.Format.ToImageFormat());
return new HttpResult(ms, request.Format.ToImageMimeType());
}
}
生のバイトを返す[]
public object Any(ImageAsBytes request)
{
using (var image = new Bitmap(100, 100))
{
using (var g = Graphics.FromImage(image))
{
g.Clear(request.Format.ToImageColor());
}
using (var m = new MemoryStream())
{
image.Save(m, request.Format.ToImageFormat());
var imageData = m.ToArray(); //buffers
return new HttpResult(imageData, request.Format.ToImageMimeType());
}
}
}
上記の例は、
Stream
および
byte[]
応答を
HttpResult
でラップすることにより、HTTP応答に追加のメタデータを追加する方法を示していますが、必要に応じて、
byte[]
、
Stream
または
直接応答します。
応答ストリームに直接書き込む
public void Any(ImageWriteToResponse request)
{
using (var image = new Bitmap(100, 100))
{
using (var g = Graphics.FromImage(image))
{
g.Clear(request.Format.ToImageColor());
}
base.Response.ContentType = request.Format.ToImageMimeType();
image.Save(base.Response.OutputStream, request.Format.ToImageFormat());
base.Response.Close();
}
}
カスタム結果を返す
public object Any(ImageAsCustomResult request)
{
var image = new Bitmap(100, 100);
using (var g = Graphics.FromImage(image))
{
g.Clear(request.Format.ToImageColor());
return new ImageResult(image, request.Format.ToImageFormat());
}
}
IStreamWriter
を実装して応答ストリームに直接書き込むことができる場所:
//Your own Custom Result, writes directly to response stream
public class ImageResult : IDisposable, IStreamWriter, IHasOptions
{
private readonly Image image;
private readonly ImageFormat imgFormat;
public ImageResult(Image image, ImageFormat imgFormat = null)
{
this.image = image;
this.imgFormat = imgFormat ?? ImageFormat.Png;
this.Options = new Dictionary<string, string> {
{ HttpHeaders.ContentType, this.imgFormat.ToImageMimeType() }
};
}
public void WriteTo(Stream responseStream)
{
using (var ms = new MemoryStream())
{
image.Save(ms, imgFormat);
ms.WriteTo(responseStream);
}
}
public void Dispose()
{
this.image.Dispose();
}
public IDictionary<string, string> Options { get; set; }
}