ASP.NET Web API向客户端发送完成后是否有通知?

7
我正在使用Web API向客户端流式传输大文件,但我想记录下载是否成功。也就是说,如果服务器发送了整个文件内容。
有没有一种方法可以在HttpResponseMessage完成数据发送时获取回调或事件?
可能像这样:
var stream = GetMyStream();
var response = new HttpResponseMessage(HttpStatusCode.OK);
response.Content = new StreamContent(stream);
response.Content.Headers.ContentType = new MediaTypeHeaderValue("application/octet-stream");    
// This doesn't exist, but it illustrates what I'm trying to do.
response.OnComplete(context =>
{
    if (context.Success)
        Log.Info("File downloaded successfully.");
    else
        Log.Warn("File download was terminated by client.");
});

哇,这正是我今天想要做的事情,八年后! - Marcel
3个回答

5

编辑:我现在已经通过fiddler使用了真实连接进行了测试。

我继承了StreamContent并添加了自己的OnComplete动作,该动作检查异常:

public class StreamContentWithCompletion : StreamContent
{
    public StreamContentWithCompletion(Stream stream) : base (stream) { }
    public StreamContentWithCompletion(Stream stream, Action<Exception> onComplete) : base(stream) 
    { 
        this.OnComplete = onComplete; 
    }

    public Action<Exception> OnComplete { get; set; }

    protected override Task SerializeToStreamAsync(Stream stream, TransportContext context)
    {
        var t = base.SerializeToStreamAsync(stream, context);
        t.ContinueWith(x =>
        {
            if (this.OnComplete != null)
            {
                // The task will be in a faulted state if something went wrong. 
                // I observed the following exception when I aborted the fiddler session: 
                // 'System.Web.HttpException (0x800704CD): The remote host closed the connection.'
                if (x.IsFaulted)
                    this.OnComplete(x.Exception.GetBaseException());
                else
                    this.OnComplete(null);
            }
        }, TaskContinuationOptions.ExecuteSynchronously);
        return t;
    }
}

然后我这样使用它:
var stream = GetMyStream();
var response = new HttpResponseMessage(HttpStatusCode.OK);
response.Content = new StreamContentWithCompletion(stream, ex =>
{
    if (ex == null)
        Log.Info("File downloaded successfully.");
    else
        Log.Warn("File download was terminated by client.");
});
response.Content.Headers.ContentType = new MediaTypeHeaderValue("application/octet-stream");    
return response;

3
我不确定是否有直接的信号表示一切正常,但是你可以使用一个技巧,在你结束连接之前和发送文件之后立即找出连接是否存在。

例如,如果客户端仍然连接,则Response.IsClientConnected将返回true,因此您可以进行以下检查:

// send the file, make a flush
Response.Flush();
// and now the file is fully sended check if the client is still connected
if(Response.IsClientConnected)
{
  // log that all looks ok until the last byte.
}
else
{
  // the client is not connected, so maybe have lost some data
}

// and now close the connection.
Response.End();

我不确定这对我在ApiController操作方法中是否有效。据我所知,除非我返回HttpResponseMessage,否则什么也不会发生。 - ligos
@ligos 你能做一个测试吗?另外我想说的是,你能否将你的数据分成小块并使用缓冲区在循环中写入,并检查最终下载的文件大小是否与原始文件大小相同? - Aristos
没错,我明天会运行一个测试。哦,而且客户端将拥有文件的SHA256哈希值,所以我已经覆盖了文件完整性。 - ligos
我已经停止使用IsClientConnected,因为a)线程问题-HttpContext在用于流内容的异步线程上不可用,b)异步任务提供异常如果有失败。(请注意,一旦解决了线程问题,如果出现错误,则IsClientConnected为false)。 - ligos
@ligos 好的,谢谢您的反馈。我决定将其保留为答案,而不是删除它,以供参考和反馈。 - Aristos

1
如果服务器发送了文件的全部内容,实际上没有什么可做的:)这听起来可能非常简单,但是如果您关心服务器提供而不是客户端在一半取消,则会知道是否引发异常。 IsClientConnected 基于 ASP.NET HttpResponse 而不是 WebApi。

明天我打算运行一些测试,但是你有在MSDN上关于异常抛出的文献参考吗? - ligos
@ligos 取决于出了什么问题。你预计会出现什么问题?网络错误吗? - Aliostad
客户端的网络掉线将是主要问题。从服务器的角度来看,这似乎是一个连接断开了。我将尝试使用 Fiddler 模拟这样的情况。 - ligos
你说得对,我的StreamContent.SerializeToStreamAsync版本存在错误任务,我已经用它来标记错误。然而,在Web API的通常情况下,这个异常不会被抛出(据我所知)。 - ligos

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接