我正在开发一个将文件上传到网络服务器IIS的Windows应用程序。当我在64位机器上运行应用程序时,我的代码运行正常。上传到IIS正在运行。但是当我在32Bit机器上运行它时,上传工作无效。
我认为这与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;
}
}
32位机器上的BytesSent始终为“0”......为什么会这样?
答案 0 :(得分:1)
在我看来,你拥有的循环有三个原因:
1)如果要求取消,您想要取消上传
2)如果有超时,您想要取消上传
3)您想知道上传的进度
我建议您完全删除循环,并以不同的方式实现这些树目标。以下是如何做到这一点:
对于(1),请使用具有overload参数的PostAsync
方法的其他CancellationToken。这允许您提供可以从其他地方使用的令牌来取消上载操作。
对于(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
会这样做,因为它需要读取整个文件才能上传所有内容,跳过部分文件是没有意义的。您可以在Seek
ReadNotifierStreamWrapper
方法上设置一些断点,看看它是否会被调用。
答案 1 :(得分:0)
你得到的例外;
EventSourceException:操作中没有可用的免费缓冲区 系统
由Debug.WriteLine
引起,它使用ETW(Windows事件跟踪)。 ETW是一个Windows内核级跟踪工具,在将数据写入磁盘之前依赖于许多缓冲区来缓存数据。 ETW使用缓冲区大小和物理内存大小来计算为事件跟踪会话的缓冲池分配的最大缓冲区数。因此,如果您的应用程序是64位ETW将分配更多缓冲区,因为可用的可寻址内存更多。
您达到限制的原因是您编写调试事件的速度比ETW处理缓冲区的速度快。
我相信可以使用注册表项增加缓冲区的最大数量,但这会增加内存消耗,并且您有可能再次使用更大的文件,更慢的连接等达到限制。
因此,最好的办法是在每个循环上调用线程以较低的速率写入跟踪事件,或者用其他形式的日志记录替换Debug.WriteLine
。