文件上传进度

4
我一直在尝试跟踪文件上传的进度,但总是陷入死胡同(从C#应用程序上传而不是网页)。
我尝试使用WebClient,如下所示:
class Program
{
    static volatile bool busy = true;

    static void Main(string[] args)
    {
        WebClient client = new WebClient();
        // Add some custom header information

        client.Credentials = new NetworkCredential("username", "password");
        client.UploadProgressChanged += client_UploadProgressChanged;
        client.UploadFileCompleted += client_UploadFileCompleted;

        client.UploadFileAsync(new Uri("http://uploaduri/"), "filename");

        while (busy)
        {
            Thread.Sleep(100);
        }
        Console.WriteLine("Done: press enter to exit");
        Console.ReadLine();
    }

    static void client_UploadFileCompleted(object sender, UploadFileCompletedEventArgs e)
    {
        busy = false;
    }

    static void client_UploadProgressChanged(object sender, UploadProgressChangedEventArgs e)
    {
        Console.WriteLine("Completed {0} of {1} bytes", e.BytesSent, e.TotalBytesToSend);
    }
}

文件已上传并打印出进度,但进度比实际上传速度快得多,当上传大文件时,进度将在几秒钟内达到最大值,但实际上传需要几分钟(它不仅是等待响应,所有数据尚未到达服务器)。
因此,我尝试使用HttpWebRequest来流式传输数据(我知道这不是文件上传的确切等效物,因为它不会产生multipart/form-data内容,但它确实可以说明我的问题)。 我将AllowWriteStreamBuffering = false设置为建议的ContentLength,如this question/answer所建议:
class Program
{
    static void Main(string[] args)
    {
        FileInfo fileInfo = new FileInfo(args[0]);
        HttpWebRequest client = (HttpWebRequest)WebRequest.Create(new Uri("http://uploadUri/"));
        // Add some custom header info
        client.Credentials = new NetworkCredential("username", "password");

        client.AllowWriteStreamBuffering = false;
        client.ContentLength = fileInfo.Length;
        client.Method = "POST";

        long fileSize = fileInfo.Length;
        using (FileStream stream = fileInfo.OpenRead())
        {
            using (Stream uploadStream = client.GetRequestStream())
            {
                long totalWritten = 0;
                byte[] buffer = new byte[3000];
                int bytesRead = 0;
                while ((bytesRead = stream.Read(buffer, 0, buffer.Length)) > 0)
                {
                    uploadStream.Write(buffer, 0, bytesRead);
                    uploadStream.Flush();
                    Console.WriteLine("{0} of {1} written", totalWritten += bytesRead, fileSize);
                }
            }
        }
        Console.WriteLine("Done: press enter to exit");
        Console.ReadLine();
    }
}

请求在整个文件被写入流并且在开始时已经显示完整进度之后才开始(我使用fiddler进行验证)。我还尝试将SendChunked设置为true(同时设置和不设置ContentLength)。似乎数据仍然会在发送到网络之前被缓存。这些方法中是否有哪一个存在问题,或者也许还有其他方法可以跟踪来自Windows应用程序的文件上传进度?

1
我和你的情况完全一样。我了解到你可以设置client.AllowReadStreamBuffering = false;,这样它就不会立即缓冲字节。这是更好的选择,但仍然不完全理想。 - Matt
在我的情况下,我可以设置 AllowReadStreamBuffering = false,这样请求就不会在所有数据“缓存”到本地之前开始。 - Cornelius
2个回答

3

更新:

这个控制台应用程序按照我的预期工作:

static ManualResetEvent done = new ManualResetEvent(false);
    static void Main(string[] args)
    {
        WebClient client = new WebClient();
        client.UploadProgressChanged += new UploadProgressChangedEventHandler(client_UploadProgressChanged);
        client.UploadFileCompleted += new UploadFileCompletedEventHandler(client_UploadFileCompleted);
        client.UploadFileAsync(new Uri("http://localhost/upload"), "C:\\test.zip");

        done.WaitOne();

        Console.WriteLine("Done");
    }

    static void client_UploadFileCompleted(object sender, UploadFileCompletedEventArgs e)
    {
        done.Set();
    }

    static void client_UploadProgressChanged(object sender, UploadProgressChangedEventArgs e)
    {
        Console.Write("\rUploading: {0}%  {1} of {2}", e.ProgressPercentage, e.BytesSent, e.TotalBytesToSend);
    }

发送一个大文件并使用Fiddler验证结果。我确实会收到进度更新(就像我的示例一样),但进度完成比文件上传完成要快得多。我的问题不是我没有收到进度更新,而是进度不正确。 - Cornelius
我的文件大约有25MB。你确认文件已经正确上传了吗?如果你给它一个不存在的URL,它会在50%左右失败(没有文件上传)。但是,如果你使用UploadFile,它会抛出异常。 - msms
文件已上传并存在服务器端。未抛出任何异常。在多台不同的计算机设置上进行了测试。 - Cornelius

1

我相信你的请求正在通过网络传输。我发现 Fiddler 2.3.4.4 不显示部分请求,但 MS Network Monitor 可以显示单个数据包,但不能在本地回环中(因此服务器和客户端需要在不同计算机上才能进行验证)。

我遇到了与这里相同的隐藏缓冲问题,并认为 WCF 服务设置之一在服务器上未正确设置流式传输。我很好奇你正在实现什么类型的 Web 服务、绑定等。基本上,服务器缓冲整个消息,然后将其交给处理程序处理,这就是为什么在客户端发送最后一个字节后可能会看到大量延迟的原因。

对于我所研究的情况,Web 服务是 WCF REST 服务,该文件在以下位置进行缓冲,然后作为流参数传递给 Web 服务方法:

C:\Windows\Microsoft.NET\Framework\v4.0.30319\Temporary ASP.NET Files\root\86e02ad6\c1702d08\uploads*.post

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