【问题标题】:Upload file from 32Bit Machine fails to IIS 7从 32 位机器上传文件到 IIS 7 失败
【发布时间】:2015-12-12 17:25:10
【问题描述】:

我正在开发一个将文件上传到网络服务器 IIS 的 Windows 应用程序。当我在 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”...这是为什么呢?

【问题讨论】:

  • "the upload is not working" 是什么意思?上传时是否出现错误?它会默默地失败吗?详细说明。
  • 它只是默默地失败了。完全没有错误...
  • 看看 IIS 中的应用程序池,你有没有将 Enable 32-Bit Applications 设置为 true?您使用的是哪种托管管道模式?
  • @DGibbs 感谢提示,它被设置为 false。我将其设置为 true,但它仍然无法正常工作...我正在使用集成管道...
  • @davidOhara。事件查看器也没有错误?

标签: c# iis upload windows-runtime windows-applications


【解决方案1】:

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

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

2)如果超时,您想取消上传

3)你想知道上传进度

我建议您完全删除循环,并以不同的方式实现这些树目标。您可以这样做:

对于 (1),使用具有 CancellationToken 参数的 PostAsync 方法的另一个 overload。这允许您提供一个令牌,您可以从其他地方使用它来取消上传操作。

对于 (2),您可以使用 CancellationTokenSource(用于创建 CancellationToken)请求在一段时间后取消上传操作(如果任务尚未完成)。请参阅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 变为真。这意味着您不希望您的方法在上传完成之前返回。在这种情况下,请使用以下命令等待上传完成。

responseTask.Wait();

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

await responseTask;

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

cancellation_token_source.Cancel();

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

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

您可以为Stream 类创建一个装饰器,它会在读取流时通知您,如下所示(请注意 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 类上做更多工作以使其变得更好。例如,HttpClient 可能出于某种原因决定通过Seek 方法查找流。您可能需要考虑到这一点。虽然,我不认为HttpClient 会这样做,因为它需要读取整个文件才能全部上传,但跳过部分文件是没有意义的。您可以在ReadNotifierStreamWrapperSeek 方法上放置一些断点,看看它是否会被调用。

【讨论】:

  • 非常好...这有很大帮助,但是 cancel_token_source.Cancel();虽然不工作。我还不知道为什么,但是在调用取消之后,Stream 仍在读取字节......
  • 所以当您调用Cancel 时,上传停止但不是立即停止?还是根本停不下来?
  • 从您要求取消到停止之间有多少时间?我认为这是 PostAsync 的行为,你要求它取消,然后它需要一段时间才能优雅地停止。
  • 是的,我已经通过 if (cancellation_token_source.IsCancellationRequested) return 解决了;感谢您的解决方案...
  • 欢迎您。我很好奇,你把if (cancellation_token_source.IsCancellationRequested) return;放在哪里了?
【解决方案2】:

你得到的异常;

EventSourceException:操作中没有可用的可用缓冲区 系统

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

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

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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-02-22
    • 2012-07-24
    • 1970-01-01
    • 2013-03-23
    相关资源
    最近更新 更多