从 32 位机器上传文件到 IIS 7 失败

Upload file from 32Bit Machine fails to IIS 7

我正在开发一个 Windows 应用程序,用于将文件上传到网络服务器 IIS。当我在 64 位机器上 运行 应用程序时,我的代码工作正常。上传到 IIS 正在工作。但是当我 运行 它在 32 位机器上时,上传不起作用。

我觉得跟IIS有关。但我不知道它可能是什么。有人遇到过同样的问题吗?

更新:这与服务器端无关。我测试了几个端点,但没有任何效果。

这一定和我的上传代码有关。此代码适用于 64 位应用程序,但不适用于 32 位应用程序:

try
        {
            System.Net.Http.HttpClient hc = new System.Net.Http.HttpClient();
            hc.DefaultRequestHeaders.TryAddWithoutValidation("Accept", "text/html,application/xhtml+xml,application/xml");
            hc.DefaultRequestHeaders.TryAddWithoutValidation("Accept-Encoding", "gzip, deflate");
            hc.DefaultRequestHeaders.TryAddWithoutValidation("User-Agent", "Mozilla/5.0 (Windows NT 6.2; WOW64; rv:19.0) Gecko/20100101 Firefox/19.0");
            hc.DefaultRequestHeaders.TryAddWithoutValidation("Accept-Charset", "ISO-8859-1");

            using (VirtualStream ms = new VirtualStream() { Size = UploadSize })
            {
                StreamContent content = new StreamContent(ms, BufferSize);

                // time for the calculation of the total average throughput
                var overallStart = DateTime.Now;

                var start = DateTime.Now;

                var responseTask = hc.PostAsync(URL, content);

                while (!responseTask.IsCompleted)
                {
                    // Check the Exit and Abort Constraints
                    if ((DateTime.Now - overallStart).TotalMilliseconds > MaxTestLength || _cancelationRequested)
                    {
                        System.Diagnostics.Debug.WriteLine("Bytes sent " + bytesSent);
                        hc.CancelPendingRequests();
                        IsRunning = false;
                        return;
                    }

                    try
                    {
                        bytesSent = ms.Position - bytesOfCalibrationPhase;
                    }
                    catch (Exception)
                    {
                        // The Upload is an async process which dispses the underlying stream when the upload finishes
                        // In some cases this could lead to ObjectDiposed Exceptions when accessing the current stream position
                        // If it is the case, the upload has finished....
                        break;
                    }


                }

BytesSent 在 32 位机器上始终为“0”...这是为什么?

你得到的异常;

EventSourceException: No Free Buffers available from the operating system

是由 Debug.WriteLine 引起的,它使用 ETW(Windows 的事件跟踪)。 ETW 是一种 Windows 内核级跟踪工具,它在将数据写入磁盘之前依赖于多个缓冲区来缓存数据。 ETW 使用缓冲区大小和物理内存大小来计算为事件跟踪会话的缓冲池分配的最大缓冲区数。因此,如果您的应用程序是 64 位,ETW 将分配更多缓冲区,因为有更多可用的可寻址内存。

您达到限制的原因是您编写调试事件的速度快于 ETW 处理缓冲区的速度。

我相信可以使用注册表项来增加缓冲区的最大数量,但这会增加内存消耗,并且您有可能在更大的文件、更慢的连接等情况下再次达到极限。 所以你最好的选择是要么在每个循环中休眠线程以较低的速率写入跟踪事件,要么用其他形式的日志记录替换 Debug.WriteLine

在我看来,您的循环存在的原因有以下三个:

1) 如果请求取消,您想取消上传

2) 超时要取消上传

3) 你想知道上传进度

我建议您完全删除循环,并以不同的方式实现这些树目标。执行此操作的方法如下:

对于 (1),使用另一个 overload of the PostAsync method that has a CancellationToken 参数。这允许您提供一个令牌,您可以从其他地方使用该令牌来取消上传操作。

对于(2),可以使用CancellationTokenSource (that you used to create the CancellationToken), to request that the upload operation be canceled after some time (if the task was not already completed). See the CancelAfter method.

这是 (1) 和 (2) 的一些代码示例:

将以下两行放在某个地方(可能作为字段),以便您的上传代码和可能希望取消上传的代码都可以使用这些变量:

CancellationTokenSource cancellation_token_source = new CancellationTokenSource();
CancellationToken cancellation_token = cancellation_token_source.Token;

下面一行代码将设置 10 秒后自动取消:

cancellation_token_source.CancelAfter(TimeSpan.FromSeconds(10));

在下一行中,我们将 cancellation_token 传递给 PostAsync 方法:

var responseTask = hc.PostAsync(URL, content, cancellation_token);

我注意到你在等待 responseTask.IsCompleted 成为现实。这意味着您不希望您的方法 return 直到上传完成。在这种情况下,使用以下等待上传完成。

responseTask.Wait();

如果您想将您的方法转换为异步方法,请将您的方法标记为 async,然后改用以下内容:

await responseTask;

您可以使用以下方式取消上传:

cancellation_token_source.Cancel();

对于(3),先看this question中的答案。

如果这对你不起作用,我有以下建议:

您可以为 Stream class 创建装饰器,在读取流时通知您,如下所示(请注意 Read 方法):

public class ReadNotifierStreamWrapper : Stream
{
    private readonly Stream m_Stream;

    private readonly Action<int> m_ReadNotifier;

    public ReadNotifierStreamWrapper(Stream stream, Action<int> read_notifier)
    {
        m_Stream = stream;
        m_ReadNotifier = read_notifier;
    }

    public override void Flush()
    {
        m_Stream.Flush();
    }

    public override long Seek(long offset, SeekOrigin origin)
    {
        return m_Stream.Seek(offset, origin);
    }

    public override void SetLength(long value)
    {
        m_Stream.SetLength(value);
    }

    public override int Read(byte[] buffer, int offset, int count)
    {
        var bytes_read = m_Stream.Read(buffer, offset, count);

        m_ReadNotifier(bytes_read);

        return bytes_read;
    }

    public override void Write(byte[] buffer, int offset, int count)
    {
        m_Stream.Write(buffer, offset, count);
    }

    public override bool CanRead
    {
        get { return m_Stream.CanRead; }
    }

    public override bool CanSeek
    {
        get { return m_Stream.CanSeek; }
    }

    public override bool CanWrite
    {
        get { return m_Stream.CanWrite; }
    }

    public override long Length
    {
        get { return m_Stream.Length; }
    }

    public override long Position
    {
        get { return m_Stream.Position; }
        set { m_Stream.Position = value; }
    }
}

然后您可以使用它来包装您的 ms 流,如下所示:

int total_bytes = 0;

var stream_wrapper = new ReadNotifierStreamWrapper(ms , bytes =>
{
    total_bytes += bytes;

    Debug.WriteLine("Bytes sent " + total_bytes);
});

HttpContent content = new StreamContent(stream_wrapper); //Here we are creating the StreamContent from stream_wrapper instead of ms

这样您将在读取流时收到通知。你会知道读取了多少字节。

请注意,您可能需要在 ReadNotifierStreamWrapper class 上做更多工作以使其变得更好。例如,可能 HttpClient 出于某种原因决定要通过 Seek 方法查找流。您可能需要考虑到这一点。虽然,我认为 HttpClient 不会这样做,因为它需要读取整个文件才能全部上传,但跳过文件的某些部分是没有意义的。你可以在ReadNotifierStreamWrapperSeek方法上打一些断点,看它是否会被调用。