ASP.Net Web API不读取StreamContent中的所有字节

时间:2012-11-23 17:42:48

标签: c# file-upload asp.net-mvc-4 asp.net-web-api

我在我的网站上设置了ASP.Net Web API,用于与WPF桌面应用程序通信。我在API上有一个动作设置来从客户端应用程序接收二进制文件。但是在某些(看似随机的)情况下,当我从请求中获取所有字节时,不会读取所有字节。希望你可以让我知道如何以一种始终有效的方式做到这一点。这是代码:

客户端:

public static SubmitTurnResult SubmitTurn(int turnId, Stream fileStream)
{
    HttpClient client = CreateHttpClient();

    HttpContent content = new StreamContent(fileStream);
    content.Headers.ContentDisposition = new ContentDispositionHeaderValue("attachment");
    content.Headers.ContentDisposition.FileName = "new-turn.Civ5Save";
    content.Headers.ContentType = new MediaTypeHeaderValue("application/octet-stream");
    content.Headers.ContentLength = fileStream.Length;

    HttpResponseMessage response = client.PostAsync(
        string.Format("SubmitTurn?authKey={0}&turnId={1}",
                        LocalSettings.Instance.AuthenticationKey,
                        turnId
                        ),
        content
    ).Result;

    response.EnsureSuccessStatusCode();

    return response.Content.ReadAsAsync<SubmitTurnResult>().Result;
}

SubmitTurnResult 是一个在服务器上定义结果的枚举, turnId 是此文件附加到的实体的ID, fileStream 是读取磁盘字节的实际FileStream。

服务器端:

[HttpGet, HttpPost]
public SubmitTurnResult SubmitTurn(string authKey, int turnId)
{

    try
    {
        bool worked = false;
        int gameId = 0;

        using (GmrEntities gmrDb = new GmrEntities())
        {
            var player = gmrDb.Users.FirstOrDefault(u => u.AuthKey == authKey);
            if (player != null)
            {
                var turn = player.Turns.FirstOrDefault(t => t.TurnID == turnId);
                if (turn != null)
                {
                    byte[] saveFileBytes = null;

                    using (MemoryStream tempStream = new MemoryStream())
                    {
                        var task = this.Request.Content.CopyToAsync(tempStream);
                        task.Wait();

                        saveFileBytes = tempStream.ToArray();
                        tempStream.Close();
                    }

                    if (saveFileBytes.Length != this.Request.Content.Headers.ContentLength.Value)
                    {
                        throw new Exception(string.Format("Byte array length ({0}) not equal to HTTP content-length header ({1}). This is not good!",
                                    saveFileBytes.Length, this.Request.Content.Headers.ContentLength.Value));
                    }

                    worked = GameManager.SubmitTurn(turn, saveFileBytes, gmrDb);

                    if (worked)
                    {
                        gameId = turn.Game.GameID;

                        gmrDb.SaveChanges();
                    }
                }
            }
        }


        return SubmitTurnResult.OK;
    }
    catch (Exception exc)
    {
        DebugLogger.WriteExceptionWithComments(exc, string.Format("Diplomacy: Sumbitting turn for turnId: {0}", turnId));

        return SubmitTurnResult.UnexpectedError;
    }
}

1 个答案:

答案 0 :(得分:11)

正如我之前的评论所述,我们遇到了与StreamContent相同的行为,但是在从Windows Server 2003 Web API服务流式传输响应时。 它不会在2008年重新发布。实际上,如果我使用少量RAM(712 MB)配置VM,它也会在Windows Server 2008上进行重新编译,但是如果配置4 GB RAM则不会摄制。此外,我们发现这只有一个带有FileStream的repros。将FileStream转换为MemoryStream会绕过问题(当然是以内存为代价)。我们发现,当响应流提前终止时,它总是在4096字节的边界上,并且它的上限大约为3.5MB。

以下是根据您的代码示例量身定制的解决方法:

public static SubmitTurnResult SubmitTurn(int turnId, Stream fileStream)
{
    HttpClient client = CreateHttpClient();

    var memoryStream = new MemoryStream((int)fileStream.Length);
    fileStream.CopyTo(memoryStream);
    fileStream.Close();
    memoryStream.Seek(0, SeekOrigin.Begin);
    HttpContent content = new StreamContent(memoryStream);

如果需要,只有在Stream是FileStream时才能有条件地执行MemoryStream复制。