通过HTTP通过WCF流式传输文件上传IOException
我正在构建托管的WCF服务,允许客户端通过HTTP上传文件。该服务按块读取客户端的Stream
块。这适用于只需要一次迭代的小文件。但是当上传大块文件后,我得到IOException
An exception has been thrown when reading the stream.
Stream.EndRead()
。通过HTTP通过WCF流式传输文件上传IOException
内部异常是The I/O operation has been aborted because of either a thread exit or an application request.
读块的数量不同,但我真的不能弄清楚,是什么原因导致的差异。它的工作时间从300ms到550ms不等,大约1MB到大约2MB处理。
有没有人有线索?
的接口被定义如下:
[ServiceContract]
public interface IServiceFileserver
{
[OperationContract]
UploadResponse UploadFile(UploadRequest uploadRequest);
// All status feedback related code is left out for simplicity
// [OperationContract]
// RunningTaskStatus GetProgress(Guid taskId);
}
[MessageContract]
public class UploadRequest
{
[MessageHeader()]
public string FileName { get; set; }
[MessageHeader()]
public long SizeInByte { get; set; }
[MessageBodyMember(Order = 1)]
public Stream Stream { get; set; }
}
[MessageContract]
public class UploadResponse
{
[MessageBodyMember()]
public Guid TaskId { get; set; }
}
这里是服务实现:
const int bufferSize = 4 * 1024;
// This is called from the client side
public UploadResponse UploadFile(UploadRequest uploadRequest)
{
Guid taskId = Guid.NewGuid();
Stream stream = null;
try
{
stream = uploadRequest.Stream;
string filename = uploadRequest.FileName;
long sizeInBytes = uploadRequest.SizeInByte;
byte[] buffer = new byte[bufferSize];
stream.BeginRead(buffer, 0, bufferSize, ReadAsyncCallback, new AsyncHelper(buffer, stream, sizeInBytes));
}
catch (Exception ex)
{
if (stream != null)
stream.Close();
}
return new UploadResponse() { TaskId = taskId };
}
// Helper class for the async reading
public class AsyncHelper
{
public Byte[] ByteArray { get; set; }
public Stream SourceStream { get; set; }
public long TotalSizeInBytes { get; set; }
public long BytesRead { get; set; }
public AsyncHelper(Byte[] array, Stream sourceStream, long totalSizeInBytes)
{
this.ByteArray = array;
this.SourceStream = sourceStream;
this.TotalSizeInBytes = totalSizeInBytes;
this.BytesRead = 0;
}
}
// Internal reading of a chunk from the stream
private void ReadAsyncCallback(IAsyncResult ar)
{
AsyncHelper info = ar.AsyncState as AsyncHelper;
int amountRead = 0;
try
{
amountRead = info.SourceStream.EndRead(ar);
}
catch (IOException ex)
{
Trace.WriteLine(ex.Message);
info.SourceStream.Close();
return;
}
// Do something with the stream
info.BytesRead += amountRead;
Trace.WriteLine("info.BytesRead: " + info.BytesRead);
if (info.SourceStream.Position < info.TotalSizeInBytes)
{
try
{ // Read next chunk from stream
info.SourceStream.BeginRead(info.ByteArray, 0, info.ByteArray.Length, ReadAsyncCallback, info);
}
catch (IOException ex)
{
info.SourceStream.Close();
}
}
else
{
info.SourceStream.Close();
}
}
绑定是这样定义:
BasicHttpBinding binding = new BasicHttpBinding();
binding.TransferMode = TransferMode.Streamed;
binding.MessageEncoding = WSMessageEncoding.Mtom;
binding.MaxReceivedMessageSize = 3 * 1024 * 1024;
binding.MaxBufferSize = 64 * 1024;
binding.CloseTimeout = new TimeSpan(0, 1, 0);
binding.OpenTimeout = new TimeSpan(0, 1, 0);
binding.ReceiveTimeout = new TimeSpan(0, 10, 0);
binding.SendTimeout = new TimeSpan(0, 1, 0);
binding.Security.Mode = BasicHttpSecurityMode.None;
binding.Security.Transport.ClientCredentialType = HttpClientCredentialType.None;
uploadRequest.Stream
是Stream
那由WCF提供。此流可能馈送WCF与您的服务的客户端维护的底层TCP连接。
此流为而不是您的服务客户端已经通过的同一对象实例。这是不可能的,因为客户端和服务器只通过TCP连接。它们不共享相同的地址空间,因此它们不能共享对象实例。
在流完全处理之前,您从UploadFile
返回。 WCF无法知道您的后台线程仍在使用此Stream对象。因此,WCF释放流的底层资源(可能会关闭到客户端的TCP连接)。
一旦您的请求处理方法返回,WCF将关闭该流。您的异步处理将会非确定性地失败。这是使用流和WCF赛车关闭它之间的线程竞赛。
该问题下的评论显示,某处存在误解,但我不完全确定它是什么。如果您需要进一步澄清,请留下评论并说出您不同意的原因以及原因。
在流完全处理之前,您正从'UploadFile'返回。你为什么期望这个工作?这是返回的函数和正在处理的流之间的竞争。 – usr 2013-05-06 20:07:44
@usr:不,我在处理异步流时返回后台任务的ID。使用该ID,客户可以向服务询问任务的进度。但为了简单起见,我在代码中省略了这部分。 – gumo 2013-05-07 06:47:30
“uploadRequest.Stream”是WCF提供的流吗?一旦您的请求处理方法返回,WCF将关闭该流。您的异步处理将会非确定性地失败。情况会是这样吗? – usr 2013-05-07 11:08:36